mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-23 08:16:05 +00:00
UI styling, main workspace layout (#119)
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
@use "sass:math";
|
||||
|
||||
:host {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
align-items: baseline;
|
||||
gap: 0.75rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
font: {
|
||||
family: "Fira Sans", sans-serif;
|
||||
size: 15px;
|
||||
size: math.div(15, 16) * 1rem;
|
||||
weight: 500;
|
||||
}
|
||||
line-height: 1.5;
|
||||
@@ -16,9 +18,8 @@
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
background: var(--background-color, #0006);
|
||||
color: #FFFC;
|
||||
backdrop-filter: blur(16px);
|
||||
background: var(--neutral-200);
|
||||
color: #FFF;
|
||||
transition: {
|
||||
property: background, color;
|
||||
duration: 0.1s;
|
||||
@@ -26,11 +27,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
background: #0008;
|
||||
color: #FFFF;
|
||||
:host(.icon-only) {
|
||||
padding-inline: 0.5rem;
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
background: var(--neutral-250);
|
||||
}
|
||||
|
||||
:host(.primary) { background: var(--color-accent) }
|
||||
:host(.primary:hover) { background: var(--color-accent-hover) }
|
||||
|
||||
:host(.secondary) { background: var(--neutral-400) }
|
||||
:host(.secondary:hover) { background: var(--neutral-450) }
|
||||
|
||||
:host(.disabled),
|
||||
:host(.disabled:hover) {
|
||||
background: #0003;
|
||||
@@ -38,6 +48,23 @@
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon {
|
||||
.icon, .dropdown-icon {
|
||||
align-self: center;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.keybind {
|
||||
// margin-inline-start: auto;
|
||||
flex: 1 0 0;
|
||||
margin-inline-start: 2.25rem;
|
||||
color: rgba(#FFF, 0.75);
|
||||
font: {
|
||||
size: 0.875rem;
|
||||
weight: 400;
|
||||
}
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { on } from "@storyteller/framework";
|
||||
import { LitElement, type PropertyValues, html, nothing, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
|
||||
import styles from "./button.element.scss?inline";
|
||||
|
||||
@@ -16,11 +17,33 @@ export class ButtonElement extends LitElement {
|
||||
@property() icon?: string;
|
||||
@property({ type: Boolean }) disabled = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
primary?: boolean;
|
||||
@property({ type: Boolean }) primary?: boolean;
|
||||
@property({ type: Boolean }) secondary?: boolean;
|
||||
@property({ type: Boolean }) dropdown?: boolean;
|
||||
|
||||
@property() keybind?: string;
|
||||
|
||||
@property({ reflect: true, type: Number })
|
||||
override tabIndex = 0;
|
||||
|
||||
@state() _hasSlottedContent = false;
|
||||
|
||||
#defaultSlotRef = createRef<HTMLSlotElement>();
|
||||
|
||||
_checkForSlotContent(): void {
|
||||
const nodes = this.#defaultSlotRef.value?.assignedNodes() ?? [];
|
||||
|
||||
this._hasSlottedContent = (
|
||||
nodes.length > 0
|
||||
&& nodes.some(node => Boolean(node.textContent?.trim()))
|
||||
);
|
||||
}
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._checkForSlotContent();
|
||||
}
|
||||
|
||||
protected override update(changes: PropertyValues<this>): void {
|
||||
if (changes.has("disabled")) {
|
||||
this.ariaDisabled = this.disabled ? "true" : null;
|
||||
@@ -28,13 +51,14 @@ export class ButtonElement extends LitElement {
|
||||
this.classList.toggle("disabled", this.disabled);
|
||||
}
|
||||
|
||||
if (changes.has("primary")) {
|
||||
if (this.primary) {
|
||||
this.style.setProperty("--background-color", "var(--color-accent)");
|
||||
} else {
|
||||
this.style.removeProperty("--background-color");
|
||||
}
|
||||
}
|
||||
if (changes.has("primary"))
|
||||
this.classList.toggle("primary", Boolean(this.primary));
|
||||
|
||||
if (changes.has("secondary"))
|
||||
this.classList.toggle("secondary", Boolean(this.secondary))
|
||||
|
||||
if (changes.has("_hasSlottedContent"))
|
||||
this.classList.toggle("icon-only", !this._hasSlottedContent);
|
||||
|
||||
super.update(changes);
|
||||
}
|
||||
@@ -50,11 +74,27 @@ export class ButtonElement extends LitElement {
|
||||
protected override render = () => html`
|
||||
${this.icon ? html`
|
||||
<sts-icon
|
||||
class="icon"
|
||||
part="icon"
|
||||
icon=${this.icon}
|
||||
aria-hidden="true"
|
||||
></sts-icon>
|
||||
` : nothing}
|
||||
<slot></slot>
|
||||
<slot
|
||||
${ref(this.#defaultSlotRef)}
|
||||
@slotchange=${this._checkForSlotContent}
|
||||
></slot>
|
||||
|
||||
${this.keybind ? html`
|
||||
<span class="keybind">${this.keybind}</span>
|
||||
` : nothing}
|
||||
|
||||
${this.dropdown ? html`
|
||||
<sts-icon
|
||||
class="dropdown-icon"
|
||||
part="dropdown-icon"
|
||||
icon="chevron-down"
|
||||
></sts-icon>
|
||||
` : nothing}
|
||||
`
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { deref, inject } from "@storyteller/framework";
|
||||
import { type ArrayOrSingle, asArray, exists } from "@storyteller/utility";
|
||||
import { LitElement, TemplateResult, type PropertyValues } from "lit";
|
||||
import { LitElement, TemplateResult, type PropertyValues, css } from "lit";
|
||||
import { html, unsafeStatic } from "lit/static-html.js";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@@ -86,6 +86,12 @@ import { type DialogBaseElement } from "./dialog-base.element";
|
||||
*/
|
||||
@customElement("sts-dialog")
|
||||
export class DialogElement extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Tag-name of a custom element inheriting from {@linkcode DialogBaseElement}`
|
||||
*/
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./form-field-base.element";
|
||||
export * from "./text-field.element";
|
||||
export * from "./scalar-field.element";
|
||||
export * from "./select";
|
||||
export * from "./text-field.element";
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import "./radio-group.element";
|
||||
import "./radio.element";
|
||||
|
||||
export * from "./radio-group.element";
|
||||
export * from "./radio.element";
|
||||
@@ -0,0 +1,4 @@
|
||||
:host {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { on } from "@storyteller/framework";
|
||||
import { PropertyValues, html, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
|
||||
import { FormFieldBaseElement } from "../form-field-base.element";
|
||||
import { RadioElement } from "./radio.element";
|
||||
|
||||
import styles from "./radio-group.element.scss?inline";
|
||||
|
||||
@customElement("sts-radio-group")
|
||||
export class RadioGroupElement extends FormFieldBaseElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@property({ reflect: true })
|
||||
override role = "radiogroup";
|
||||
|
||||
@property({ attribute: false })
|
||||
value?: any;
|
||||
|
||||
#slotRef = createRef<HTMLSlotElement>();
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.#checkValue();
|
||||
}
|
||||
|
||||
protected override update(changes: PropertyValues<this>): void {
|
||||
if (changes.has("value"))
|
||||
this.#checkValue();
|
||||
|
||||
super.update(changes);
|
||||
}
|
||||
|
||||
@on("click", { capture: true })
|
||||
onClick(event: PointerEvent): void {
|
||||
if (event.target instanceof RadioElement) {
|
||||
this.dispatchEvent(new CustomEvent("value-change", {
|
||||
detail: event.target.value,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#checkValue(): void {
|
||||
for (let node of this.#slotRef.value?.assignedElements() ?? []) {
|
||||
if (node instanceof RadioElement) {
|
||||
node.checked = node.value != null && node.value === this.value;
|
||||
|
||||
if (node.checked)
|
||||
this.internals.ariaValueText = node.textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
<slot
|
||||
${ref(this.#slotRef)}
|
||||
@slotchange=${this.#checkValue}
|
||||
></slot>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
:host {
|
||||
position: relative;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
color: #FFF;
|
||||
outline: transparent 2px solid;
|
||||
|
||||
transition: {
|
||||
property: background, outline-color;
|
||||
duration: 100ms;
|
||||
timing-function: linear;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
background: var(--neutral-250);
|
||||
}
|
||||
|
||||
:host(.checked) {
|
||||
outline-color: var(--color-accent);
|
||||
}
|
||||
|
||||
:host(.checked:hover) {
|
||||
background: var(--color-accent-fade);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import styles from "./radio.element.scss?inline";
|
||||
|
||||
@customElement("sts-radio")
|
||||
export class RadioElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@property({ reflect: true })
|
||||
override role = "radio";
|
||||
|
||||
@property({ attribute: false })
|
||||
value?: any;
|
||||
|
||||
@property({ type: Boolean })
|
||||
checked = false;
|
||||
|
||||
protected override update(changes: PropertyValues<this>): void {
|
||||
if (changes.has("checked"))
|
||||
this.classList.toggle("checked", this.checked);
|
||||
|
||||
super.update(changes);
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
@@ -4,28 +4,33 @@
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.25rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
background: var(--neutral-200);
|
||||
border: 1px solid transparent;
|
||||
color: #FFF;
|
||||
outline: transparent 2px solid {
|
||||
offset: -2px;
|
||||
}
|
||||
|
||||
background: #0004;
|
||||
border: 1px solid #FFF2;
|
||||
color: #FFFC;
|
||||
outline: none !important;
|
||||
transition: {
|
||||
property: background-color, border-color, color;
|
||||
duration: 33.333ms;
|
||||
property: background-color, outline-color;
|
||||
duration: 100ms;
|
||||
timing-function: linear;
|
||||
}
|
||||
|
||||
font: {
|
||||
family: "Fira Sans", sans-serif;
|
||||
size: math.div(15, 16) * 1rem;
|
||||
size: math.div(14, 16) * 1rem;
|
||||
weight: 500;
|
||||
}
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:host(:focus-within) {
|
||||
border-color: #FFF4;
|
||||
background: #0006;
|
||||
color: #FFFF;
|
||||
outline-color: var(--color-accent);
|
||||
background: var(--neutral-100)
|
||||
}
|
||||
|
||||
:host(:not(.editing)) {
|
||||
@@ -45,4 +50,8 @@
|
||||
&:first-line {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
background: var(--neutral-300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import "./select.element";
|
||||
import "./option.element";
|
||||
|
||||
export * from "./select.element";
|
||||
export * from "./option.element";
|
||||
@@ -0,0 +1,7 @@
|
||||
:host {
|
||||
padding: 0.375rem 0;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--neutral-350);
|
||||
transition: background 100ms linear;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { LitElement, html, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import styles from "./listbox.element.scss?inline";
|
||||
|
||||
@customElement("sts-listbox")
|
||||
export class ListboxElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@property({ reflect: true })
|
||||
override role = "listbox";
|
||||
|
||||
protected override render = () => html`
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
@use "sass:math";
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
|
||||
color: #FFF;
|
||||
transition: background 100ms linear;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
font-size: math.div(15, 16) * 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
background: rgba(#FFF, 0.1);
|
||||
}
|
||||
|
||||
:host(.selected) {
|
||||
background: var(--neutral-200);
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { LitElement, PropertyValues, html, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import styles from "./option.element.scss?inline";
|
||||
|
||||
@customElement("sts-option")
|
||||
export class OptionElement extends LitElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@property({ reflect: true })
|
||||
override role = "option";
|
||||
|
||||
@property({ attribute: false })
|
||||
value?: any;
|
||||
|
||||
@property({ type: Boolean })
|
||||
selected?: boolean;
|
||||
|
||||
protected override update(changes: PropertyValues<this>): void {
|
||||
if (changes.has("selected")) {
|
||||
this.classList.toggle("selected", this.selected);
|
||||
|
||||
if (this.selected)
|
||||
this.setAttribute("aria-selected", "true")
|
||||
else
|
||||
this.removeAttribute("aria-selected");
|
||||
}
|
||||
|
||||
super.update(changes);
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
@use "sass:math";
|
||||
|
||||
:host {
|
||||
padding: 0.5rem;
|
||||
background: var(--neutral-350);
|
||||
color: #FFF;
|
||||
|
||||
transition: {
|
||||
property: background;
|
||||
duration: 100ms;
|
||||
timing-function: linear;
|
||||
}
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
font-size: math.div(15, 16) * 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
:host(:hover) {
|
||||
background: var(--neutral-400);
|
||||
}
|
||||
|
||||
.selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
|
||||
sts-option {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { inject, on } from "@storyteller/framework";
|
||||
import { PropertyValues, html, nothing, unsafeCSS } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { styleMap } from "lit/directives/style-map.js";
|
||||
|
||||
import {
|
||||
OVERLAY_PROVIDER,
|
||||
OverlayHandle,
|
||||
type OverlayProvider,
|
||||
} from "../../overlay-provider.element";
|
||||
import { FormFieldBaseElement } from "../form-field-base.element";
|
||||
import { OptionElement } from "./option.element";
|
||||
|
||||
import "./listbox.element";
|
||||
|
||||
import styles from "./select.element.scss?inline";
|
||||
|
||||
@customElement("sts-select")
|
||||
export class SelectElement extends FormFieldBaseElement {
|
||||
static override styles = unsafeCSS(styles);
|
||||
|
||||
@property({ reflect: true })
|
||||
override role = "combobox";
|
||||
|
||||
@property({ attribute: false })
|
||||
value?: any;
|
||||
|
||||
@property()
|
||||
placeholder?: string;
|
||||
|
||||
@property({ type: Number, reflect: true })
|
||||
override tabIndex = 0;
|
||||
|
||||
@inject(OVERLAY_PROVIDER)
|
||||
_overlay!: OverlayProvider
|
||||
|
||||
@state() _selected?: OptionElement;
|
||||
|
||||
#slotRef = createRef<HTMLSlotElement>();
|
||||
#overlayHandle?: WeakRef<OverlayHandle>;
|
||||
|
||||
get #selectedOption() {
|
||||
return this.#slotRef.value
|
||||
?.assignedNodes()
|
||||
.find((node: Node): node is OptionElement => (
|
||||
node.nodeType === Node.ELEMENT_NODE
|
||||
&& node instanceof OptionElement
|
||||
&& node.value === this.value
|
||||
));
|
||||
}
|
||||
|
||||
override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.#checkValue();
|
||||
}
|
||||
|
||||
protected override update(changes: PropertyValues<this>): void {
|
||||
if (changes.has("value"))
|
||||
this.#checkValue();
|
||||
|
||||
super.update(changes);
|
||||
}
|
||||
|
||||
@on("click")
|
||||
onClick(): void {
|
||||
const nodes = this.#slotRef.value
|
||||
?.assignedNodes()
|
||||
.map(node => {
|
||||
const cloned = node.cloneNode(true);
|
||||
if (
|
||||
cloned.nodeType === Node.ELEMENT_NODE
|
||||
&& cloned instanceof OptionElement
|
||||
) {
|
||||
cloned.value = (node as OptionElement).value;
|
||||
cloned.selected = cloned.value === this.value;
|
||||
}
|
||||
|
||||
return cloned;
|
||||
});
|
||||
|
||||
const rect = this.getBoundingClientRect();
|
||||
|
||||
this.#overlayHandle = this._overlay.attach(this, html`
|
||||
<sts-listbox
|
||||
style=${styleMap({
|
||||
position: "absolute",
|
||||
top: `${rect.bottom + 4}px`,
|
||||
left: `${rect.left}px`,
|
||||
minWidth: `${rect.width}px`,
|
||||
})}
|
||||
@click=${this.onListboxClick}
|
||||
>
|
||||
${nodes ?? nothing}
|
||||
</sts-listbox>
|
||||
`, {
|
||||
onBackdropClick: () => this.#close(),
|
||||
});
|
||||
}
|
||||
|
||||
onListboxClick(event: PointerEvent): void {
|
||||
if (event.target instanceof OptionElement) {
|
||||
this.dispatchEvent(new CustomEvent("value-change", {
|
||||
detail: event.target.value,
|
||||
}));
|
||||
|
||||
this.#close();
|
||||
}
|
||||
}
|
||||
|
||||
#checkValue(): void {
|
||||
const cloned = this.#selectedOption?.cloneNode(true) as OptionElement | undefined;
|
||||
|
||||
if (cloned) {
|
||||
this.internals.ariaValueText = cloned.textContent;
|
||||
|
||||
cloned.removeAttribute("role");
|
||||
cloned.style.setProperty("pointer-events", "none");
|
||||
this._selected = cloned;
|
||||
}
|
||||
}
|
||||
|
||||
#close(): void {
|
||||
if (!this.#overlayHandle) return;
|
||||
this._overlay.detach(this.#overlayHandle);
|
||||
this.#overlayHandle = undefined;
|
||||
}
|
||||
|
||||
protected override render = () => html`
|
||||
<div class="selected">
|
||||
${this._selected ?? html`
|
||||
<div class="placeholder">
|
||||
${this.placeholder ?? nothing}
|
||||
</div>
|
||||
`}
|
||||
<sts-icon
|
||||
class="dropdown-icon"
|
||||
icon="chevron-down"
|
||||
></sts-icon>
|
||||
</div>
|
||||
<div class="hidden">
|
||||
<slot
|
||||
${ref(this.#slotRef)}
|
||||
@slotchange=${this.#checkValue}
|
||||
></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -4,13 +4,12 @@ import { LitElement, TemplateResult, html, nothing, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { unsafeSVG } from "lit/directives/unsafe-svg.js";
|
||||
|
||||
import farCrosshairs from "@fortawesome/fontawesome-pro/svgs/regular/crosshairs.svg?raw";
|
||||
import farVideo from "@fortawesome/fontawesome-pro/svgs/regular/video.svg?raw";
|
||||
import fasCrosshairs from "@fortawesome/fontawesome-pro/svgs/solid/crosshairs.svg?raw";
|
||||
import fasVideo from "@fortawesome/fontawesome-pro/svgs/solid/video.svg?raw";
|
||||
import fasCircleQuestion from "@fortawesome/fontawesome-pro/svgs/solid/circle-question.svg?raw";
|
||||
import fasKey from "@fortawesome/fontawesome-pro/svgs/solid/key.svg?raw";
|
||||
import farCube from "@fortawesome/fontawesome-pro/svgs/regular/cube.svg?raw";
|
||||
import farGlobe from "@fortawesome/fontawesome-pro/svgs/regular/globe.svg?raw";
|
||||
import farUpDownLeftRight from "@fortawesome/fontawesome-pro/svgs/regular/arrows-up-down-left-right.svg?raw";
|
||||
import fasGlobe from "@fortawesome/fontawesome-pro/svgs/solid/globe.svg?raw";
|
||||
import fasUpDownLeftRight from "@fortawesome/fontawesome-pro/svgs/solid/arrows-up-down-left-right.svg?raw";
|
||||
import fasPlay from "@fortawesome/fontawesome-pro/svgs/solid/play.svg?raw";
|
||||
import fasPause from "@fortawesome/fontawesome-pro/svgs/solid/pause.svg?raw";
|
||||
import fasFwdStep from "@fortawesome/fontawesome-pro/svgs/solid/forward-step.svg?raw";
|
||||
@@ -18,12 +17,12 @@ import fasFwdFast from "@fortawesome/fontawesome-pro/svgs/solid/forward-fast.svg
|
||||
import fasBwdStep from "@fortawesome/fontawesome-pro/svgs/solid/backward-step.svg?raw";
|
||||
import fasBwdFast from "@fortawesome/fontawesome-pro/svgs/solid/backward-fast.svg?raw";
|
||||
import fasQuestion from "@fortawesome/fontawesome-pro/svgs/solid/question.svg?raw";
|
||||
import farRotate from "@fortawesome/fontawesome-pro/svgs/regular/arrows-rotate.svg?raw";
|
||||
import farScale from "@fortawesome/fontawesome-pro/svgs/regular/arrow-up-right-and-arrow-down-left-from-center.svg?raw";
|
||||
import fasRotate from "@fortawesome/fontawesome-pro/svgs/solid/arrows-rotate.svg?raw";
|
||||
import fasScale from "@fortawesome/fontawesome-pro/svgs/solid/arrow-up-right-and-arrow-down-left-from-center.svg?raw";
|
||||
import fasCaretRight from "@fortawesome/fontawesome-pro/svgs/solid/caret-right.svg?raw";
|
||||
import fasFloppyDisk from "@fortawesome/fontawesome-pro/svgs/solid/floppy-disk.svg?raw";
|
||||
import farSkeleton from "@fortawesome/fontawesome-pro/svgs/regular/skeleton.svg?raw";
|
||||
import farBone from "@fortawesome/fontawesome-pro/svgs/regular/bone.svg?raw";
|
||||
import fasSkeleton from "@fortawesome/fontawesome-pro/svgs/regular/skeleton.svg?raw";
|
||||
import fasBone from "@fortawesome/fontawesome-pro/svgs/regular/bone.svg?raw";
|
||||
import fasBrightness from "@fortawesome/fontawesome-pro/svgs/solid/brightness.svg?raw";
|
||||
import fasCheck from "@fortawesome/fontawesome-pro/svgs/solid/check.svg?raw";
|
||||
import fasXmark from "@fortawesome/fontawesome-pro/svgs/solid/xmark.svg?raw";
|
||||
@@ -33,23 +32,29 @@ import fasMinus from "@fortawesome/fontawesome-pro/svgs/solid/minus.svg?raw";
|
||||
import fasListTree from "@fortawesome/fontawesome-pro/svgs/solid/list-tree.svg?raw";
|
||||
import fasBars from "@fortawesome/fontawesome-pro/svgs/solid/bars.svg?raw";
|
||||
import fasPlusLarge from "@fortawesome/fontawesome-pro/svgs/solid/plus-large.svg?raw";
|
||||
import farFileImport from "@fortawesome/fontawesome-pro/svgs/regular/file-import.svg?raw";
|
||||
import farPhotoFilmMusic from "@fortawesome/fontawesome-pro/svgs/regular/photo-film-music.svg?raw";
|
||||
import fasFileImport from "@fortawesome/fontawesome-pro/svgs/regular/file-import.svg?raw";
|
||||
import fasPhotoFilmMusic from "@fortawesome/fontawesome-pro/svgs/regular/photo-film-music.svg?raw";
|
||||
import fasChevronDown from "@fortawesome/fontawesome-pro/svgs/solid/chevron-down.svg?raw";
|
||||
import farVolume from "@fortawesome/fontawesome-pro/svgs/regular/volume.svg?raw";
|
||||
import farFilm from "@fortawesome/fontawesome-pro/svgs/regular/film.svg?raw";
|
||||
import fasVolume from "@fortawesome/fontawesome-pro/svgs/regular/volume.svg?raw";
|
||||
import fasFilm from "@fortawesome/fontawesome-pro/svgs/regular/film.svg?raw";
|
||||
import fasPersonRunning from "@fortawesome/fontawesome-pro/svgs/solid/person-running.svg?raw";
|
||||
import farMountainSun from "@fortawesome/fontawesome-pro/svgs/regular/mountain-sun.svg?raw";
|
||||
import farArrowRightToBracket from "@fortawesome/fontawesome-pro/svgs/regular/arrow-right-to-bracket.svg?raw";
|
||||
import farMagnifyingGlass from "@fortawesome/fontawesome-pro/svgs/regular/magnifying-glass.svg?raw";
|
||||
import fasMountainSun from "@fortawesome/fontawesome-pro/svgs/regular/mountain-sun.svg?raw";
|
||||
import fasArrowRightToBracket from "@fortawesome/fontawesome-pro/svgs/regular/arrow-right-to-bracket.svg?raw";
|
||||
import fasMagnifyingGlass from "@fortawesome/fontawesome-pro/svgs/regular/magnifying-glass.svg?raw";
|
||||
import fasHouse from "@fortawesome/fontawesome-pro/svgs/solid/house.svg?raw";
|
||||
import fasRecord from "@fortawesome/fontawesome-pro/svgs/solid/circle.svg?raw";
|
||||
import fasUpload from "@fortawesome/fontawesome-pro/svgs/solid/upload.svg?raw";
|
||||
import fasFilms from "@fortawesome/fontawesome-pro/svgs/solid/films.svg?raw";
|
||||
import fasFile from "@fortawesome/fontawesome-pro/svgs/solid/file.svg?raw";
|
||||
import fasTrash from "@fortawesome/fontawesome-pro/svgs/solid/trash.svg?raw";
|
||||
import fasRightLeft from "@fortawesome/fontawesome-pro/svgs/solid/right-left.svg?raw";
|
||||
import fasChevronRight from "@fortawesome/fontawesome-pro/svgs/solid/chevron-right.svg?raw";
|
||||
|
||||
import customCylinder from "../assets/icons/cylinder.svg?raw";
|
||||
import customSphere from "../assets/icons/sphere.svg?raw";
|
||||
import customTorus from "../assets/icons/torus.svg?raw";
|
||||
import customCube from "../assets/icons/cube-solid.svg?raw";
|
||||
import customCylinder from "../assets/icons/cylinder-solid.svg?raw";
|
||||
import customSphere from "../assets/icons/sphere-solid.svg?raw";
|
||||
import customTorus from "../assets/icons/torus-solid.svg?raw";
|
||||
import customXformLocal from "../assets/icons/xform-local.svg?raw";
|
||||
|
||||
import styles from "./icon.element.scss?inline";
|
||||
|
||||
@@ -77,49 +82,54 @@ function renderSvg(iconId: string) {
|
||||
|
||||
const svgSrc = match (iconId, {
|
||||
"bars": () => fasBars,
|
||||
"bone": () => farBone,
|
||||
"bone": () => fasBone,
|
||||
"bwd-fast": () => fasBwdFast,
|
||||
"bwd-step": () => fasBwdStep,
|
||||
"caret-right": () => fasCaretRight,
|
||||
"check": () => fasCheck,
|
||||
"chevron-down": () => fasChevronDown,
|
||||
"chevron-right": () => fasChevronRight,
|
||||
"circle-check": () => fasCircleCheck,
|
||||
"circle-question": () => fasCircleQuestion,
|
||||
"circle-xmark": () => fasCircleXmark,
|
||||
"crosshairs": () => farCrosshairs,
|
||||
"cube": () => farCube,
|
||||
"crosshairs": () => fasCrosshairs,
|
||||
"cube": () => customCube,
|
||||
"cylinder": () => customCylinder,
|
||||
"figure": () => fasPersonRunning,
|
||||
"file-import": () => farFileImport,
|
||||
"film": () => farFilm,
|
||||
"file": () => fasFile,
|
||||
"file-import": () => fasFileImport,
|
||||
"film": () => fasFilm,
|
||||
"films": () => fasFilms,
|
||||
"floppy-disk": () => fasFloppyDisk,
|
||||
"fwd-fast": () => fasFwdFast,
|
||||
"fwd-step": () => fasFwdStep,
|
||||
"home": () => fasHouse,
|
||||
"import": () => farArrowRightToBracket,
|
||||
"import": () => fasArrowRightToBracket,
|
||||
"key": () => fasKey,
|
||||
"light": () => fasBrightness,
|
||||
"list-tree": () => fasListTree,
|
||||
"minus": () => fasMinus,
|
||||
"globe": () => farGlobe,
|
||||
"photo-film-music": () => farPhotoFilmMusic,
|
||||
"globe": () => fasGlobe,
|
||||
"photo-film-music": () => fasPhotoFilmMusic,
|
||||
"play": () => fasPlay,
|
||||
"pause": () => fasPause,
|
||||
"plus-large": () => fasPlusLarge,
|
||||
"question": () => fasQuestion,
|
||||
"record": () => fasRecord,
|
||||
"rotate": () => farRotate,
|
||||
"scale": () => farScale,
|
||||
"scene": () => farMountainSun,
|
||||
"search": () => farMagnifyingGlass,
|
||||
"skeleton": () => farSkeleton,
|
||||
"rotate": () => fasRotate,
|
||||
"scale": () => fasScale,
|
||||
"scene": () => fasMountainSun,
|
||||
"search": () => fasMagnifyingGlass,
|
||||
"skeleton": () => fasSkeleton,
|
||||
"sphere": () => customSphere,
|
||||
"swap": () => fasRightLeft,
|
||||
"trash": () => fasTrash,
|
||||
"torus": () => customTorus,
|
||||
"up-down-left-right": () => farUpDownLeftRight,
|
||||
"up-down-left-right": () => fasUpDownLeftRight,
|
||||
"upload": () => fasUpload,
|
||||
"video": () => farVideo,
|
||||
"volume": () => farVolume,
|
||||
"video": () => fasVideo,
|
||||
"volume": () => fasVolume,
|
||||
"xform-local": () => customXformLocal,
|
||||
"xmark": () => fasXmark,
|
||||
_: () => null,
|
||||
});
|
||||
|
||||
@@ -5,24 +5,17 @@
|
||||
padding: 0.5rem 0;
|
||||
min-width: 220px;
|
||||
|
||||
background: #0006;
|
||||
backdrop-filter: blur(16px);
|
||||
background: var(--neutral-200);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
::slotted(sts-button) {
|
||||
background: unset;
|
||||
backdrop-filter: unset;
|
||||
border-radius: unset;
|
||||
}
|
||||
|
||||
::slotted(sts-button:hover) {
|
||||
background: #0004;
|
||||
|
||||
// FIXME: https://github.com/w3c/csswg-drafts/issues/3896
|
||||
&::part(icon) {
|
||||
opacity: 0.75;
|
||||
}
|
||||
background: var(--neutral-300);
|
||||
}
|
||||
|
||||
::slotted(hr) {
|
||||
@@ -31,5 +24,5 @@
|
||||
border: none;
|
||||
height: 1px;
|
||||
margin: 0.5rem 0;
|
||||
background: #FFF1;
|
||||
background: rgba(#FFF, 0.1);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ import panelStyles from "./menu.element.scss?inline";
|
||||
export class MenuElement extends LitElement {
|
||||
static override styles = css`
|
||||
:host {
|
||||
visibility: hidden;
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,22 +1,8 @@
|
||||
:host {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
::slotted(*) {
|
||||
border-radius: var(--corner-radius);
|
||||
}
|
||||
|
||||
::slotted(:first-child) {
|
||||
border: {
|
||||
top-left-radius: 8px;
|
||||
bottom-left-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
::slotted(:last-child) {
|
||||
border: {
|
||||
top-right-radius: 8px;
|
||||
bottom-right-radius: 8px;
|
||||
}
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
:host {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background: var(--neutral-200);
|
||||
}
|
||||
|
||||
::slotted(hr) {
|
||||
display: block;
|
||||
width: 2px;
|
||||
height: 1.125rem;
|
||||
border: none;
|
||||
background: rgba(#FFF, 0.1);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type DynamicProvider, inject, UniqueToken } from "@storyteller/framework";
|
||||
import { LitElement, type PropertyValues, html, nothing, unsafeCSS } from "lit";
|
||||
import { type DynamicProvider, inject, UniqueToken, observe } from "@storyteller/framework";
|
||||
import { LitElement, 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";
|
||||
@@ -34,23 +34,9 @@ export class TreeElement extends LitElement {
|
||||
return !!this.id && this._selectionProvider?.selectedId === this.id
|
||||
}
|
||||
|
||||
@observe(["selectedId"])
|
||||
@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();
|
||||
}
|
||||
_selectionProvider!: TreeSelectionProvider;
|
||||
|
||||
toggleExpanded(): void {
|
||||
this.expanded = !this.expanded;
|
||||
|
||||
Reference in New Issue
Block a user