Skip to content

Commit

Permalink
save button collapse and smaller logo on small screens
Browse files Browse the repository at this point in the history
  • Loading branch information
sdumetz committed Oct 23, 2024
1 parent ebbda0b commit fa66165
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 14 deletions.
102 changes: 102 additions & 0 deletions libs/ff-ui/source/Dropdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* FF Typescript Foundation Library
* Copyright 2019 Ralph Wiedemeier, Frame Factory GmbH
*
* License: MIT
*/

import { customElement, property, html, PropertyValues } from "./CustomElement";

import Button from "./Button";
import "./Menu";
import { IMenuItem } from "./Menu";

////////////////////////////////////////////////////////////////////////////////

export type DropdownDirection = "up" | "down";
export type DropdownAlign = "left" | "right";

@customElement("ff-dropdown")
export default class Dropdown extends Button
{
/** Direction of the dropdown menu. Possible values: "down" (default), "up". */
@property({ type: String })
direction: DropdownDirection = "down";

@property({ type: String })
align: DropdownAlign = "left";

/** Items to be displayed in the dropdown menu. */
@property({ attribute: false })
items: Array<IMenuItem | string> = [];

@property({ type: Number })
itemIndex = -1;


constructor()
{
super();
this.caret = true;

this.onKeyOrPointer = this.onKeyOrPointer.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
}

protected firstConnected()
{
super.firstConnected();
this.classList.add("ff-dropdown");
}

protected connected()
{
super.connected();
document.addEventListener("pointerdown", this.onKeyOrPointer, { capture: true, passive: true });
document.addEventListener("keyup", this.onKeyOrPointer, { capture: true, passive: true });
}

protected disconnected()
{
super.disconnected();
document.removeEventListener("pointerdown", this.onKeyOrPointer);
document.removeEventListener("keyup", this.onKeyOrPointer);
}

protected render()
{
const classes = (this.direction === "up" ? "ff-position-above " : "ff-position-below ")
+ (this.align === "right" ? "ff-align-right" : "ff-align-left");

const menu = this.selected ? html`<ff-menu class=${classes} .items=${this.items} itemIndex=${this.itemIndex} setFocus></ff-menu>` : null;
return html`${super.render()}${menu}`;
}

protected onClick(event: MouseEvent)
{
this.selected = !this.selected;
if (!this.selected) {
setTimeout(() => this.focus(), 0);
}
}



protected onKeyDown(event: KeyboardEvent)
{
super.onKeyDown(event);

// on escape key close the dropdown menu
if (event.code === "Escape" && this.selected) {
this.selected = false;
}
}

protected onKeyOrPointer(event: UIEvent)
{
// if pointer goes down outside this close the dropdown menu
if (this.selected && !(event.target instanceof Node && this.contains(event.target))) {
this.selected = false;
}
}
}
142 changes: 142 additions & 0 deletions libs/ff-ui/source/Menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* FF Typescript Foundation Library
* Copyright 2019 Ralph Wiedemeier, Frame Factory GmbH
*
* License: MIT
*/

import "./Button";
import { IButtonClickEvent, IButtonKeyboardEvent } from "./Button";

import CustomElement, { customElement, property, html } from "./CustomElement";

////////////////////////////////////////////////////////////////////////////////

export interface IMenuItem
{
index?: number;
name?: string;
text?: string;
icon?: string;
checked?: boolean;
disabled?: boolean;
divider?: boolean;
selectedIndex?: number;
selected?: boolean;
}

export interface IMenuSelectEvent extends CustomEvent
{
type: "select";
target: Menu;
detail: {
item: IMenuItem;
}
}

@customElement("ff-menu")
export default class Menu extends CustomElement
{
static readonly iconChecked = "fas fa-check";

/** Optional name to identify the dropdown. */
@property({ type: String })
name = "";

/** Optional index to identify the dropdown. */
@property({ type: Number })
index = 0;

/** Entries to be displayed in the dropdown menu. */
@property({ attribute: false })
items: Array<IMenuItem | string> = null;

@property({ type: Number })
itemIndex = -1;

@property({ type: Boolean })
setFocus = false;


protected firstConnected()
{
this.setAttribute("role", "menu");
this.classList.add("ff-menu");
}

protected render()
{
if (!this.items) {
return html``;
}

return html`${this.items.map((item, index) => this.renderItem(item, index))}`;
}

protected renderItem(item: IMenuItem | string, index: number)
{
let text, icon;

if (typeof item === "string") {
text = item;
icon = "empty";
}
else if (item.divider) {
return html`<div class="ff-divider"></div>`;
}
else {
text = item.text;
icon = item.icon || (item.checked ? "check" : "empty");
}

return html`<ff-button index=${index} selectedIndex=${this.itemIndex}
icon=${icon} text=${text} @click=${this.onClick} @keydown=${this.onKeyDown}></ff-button>`;
}

updated()
{
if (this.setFocus) {
const index = this.itemIndex >= 0 ? this.itemIndex : 0;
this.focusItem(index);
}
}

protected focusItem(index: number)
{
const child = this.children.item(index);

if (child instanceof HTMLElement) {
child.focus();
}
}

protected onClick(event: IButtonClickEvent)
{
const item = this.items[event.target.index];

if (!item) {
return;
}

this.dispatchEvent(new CustomEvent("select", {
detail: { item },
bubbles: true
}) as IMenuSelectEvent);
}

protected onKeyDown(event: IButtonKeyboardEvent)
{
const items = this.items;

if (event.code === "ArrowDown") {
let index = event.target.index;
do { index = (index + 1) % items.length } while (items[index]["divider"]);
this.focusItem(index);
}
else if (event.code === "ArrowUp") {
let index = event.target.index;
do { index = (index + items.length - 1) % items.length } while (items[index]["divider"]);
this.focusItem(index);
}
}
}
7 changes: 7 additions & 0 deletions libs/ff-ui/source/styles/_styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ button, input {
overflow-y: auto;
}

.ff-scroll-x{
overflow-x: auto;
}

.ff-position-above {
position: absolute;
bottom: 0;
Expand Down Expand Up @@ -486,6 +490,9 @@ button, input {
justify-content: flex-start;
margin: 0;
padding: 4px 4px;
&.ff-control{
flex-wrap: nowrap;
}

.ff-icon {
height: 1.2em;
Expand Down
51 changes: 40 additions & 11 deletions source/client/ui/story/TaskBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
import System from "@ff/graph/System";

import "@ff/ui/Button";
import "@ff/ui/Dropdown";
import Button, { IButtonClickEvent } from "@ff/ui/Button";

import SystemView, { customElement, html } from "@ff/scene/ui/SystemView";

import CVStoryApplication from "../../components/CVStoryApplication";
import CVTaskProvider, { ETaskMode, IActiveTaskEvent, ITaskSetEvent } from "../../components/CVTaskProvider";
import CVAssetReader from "../../components/CVAssetReader";
import CVLanguageManager from "client/components/CVLanguageManager";
import { IMenuItem } from "@ff/ui/Menu";
import CVSetup from "client/components/CVSetup";


////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -77,24 +79,39 @@ export default class TaskBar extends SystemView
const activeTask = this.taskProvider.activeComponent;
const taskMode = this.taskProvider.ins.mode.value;
const taskModeText = this.taskProvider.ins.mode.getOptionText();
const downloadButtonVisible = taskMode !== ETaskMode.Standalone;
const exitButtonVisible = taskMode !== ETaskMode.Standalone;
const language = this.language;
const saveName = language.getLocalizedString(taskMode !== ETaskMode.Standalone ? "Save" : "Download");

const saveOptions :IMenuItem[] = [
{name: "download", icon:"download", text:language.getLocalizedString("Download")}
];
if(taskMode !== ETaskMode.Standalone){
saveOptions.unshift(
{name: "save", icon: "save", text: saveName},
{name: "capture", icon: "save", text: language.getLocalizedString("Save Setup")},
);
}

return html`
<img class="sv-story-logo" src=${this.assetReader.getSystemAssetUrl("images/voyager-75grey.svg")} alt="Logo"/>
<div class="sv-mode ff-text">${taskModeText}</div>
<sv-logo .assetPath=${this.assetReader.getSystemAssetUrl("")}></sv-logo>
<div class="sv-mode ff-text">
<span class="sv-mode-sm">${taskModeText.slice(0, 2)}</span>
<span class="sv-mode-lg">${taskModeText}</span>
</div>
<div class="sv-spacer"></div>
<div class="sv-divider"></div>
<div class="ff-flex-row ff-group" @click=${this.onClickTask}>
<div class="ff-flex-row ff-group ff-scroll-x" @click=${this.onClickTask}>
${tasks.map((task, index) => html`<ff-button text=${language.getLocalizedString(task.text)} icon=${task.icon} index=${index} ?selected=${task === activeTask}></ff-button>`)}
</div>
<div class="sv-divider"></div>
<div class="sv-spacer"></div>
<div class="sv-divider"></div>
<div class="ff-flex-row ff-group">
<ff-button text=${saveName} icon="save" @click=${this.onClickSave}></ff-button>
${downloadButtonVisible ? html`<ff-button text="${language.getLocalizedString("Download")}" icon="download" @click=${this.onClickDownload}></ff-button>` : null}
<div class="ff-flex-row ff-group" style="min-width:100px">
${1 < saveOptions.length?
html`<ff-dropdown caret text="${saveName}" icon="save" @select=${this.onSelectSave} .items=${saveOptions}></ff-dropdown>`
: html`<ff-button text="${saveOptions[0].text}" icon="${saveOptions[0].icon}" @click=${()=>this.onSelectSave(new CustomEvent("select", {detail: {item: saveOptions[0]}}))}></ff-button>`
}
${exitButtonVisible ? html`<ff-button text="${language.getLocalizedString("Exit")}" icon="exit" @click=${this.onClickExit}></ff-button>` : null}
</div>
`;
Expand All @@ -108,9 +125,21 @@ export default class TaskBar extends SystemView
}
}

protected onClickSave()
{
this.story.ins.save.set();
protected onSelectSave(event :{detail: {item: IMenuItem}}){
switch(event.detail.item.name){
case "save":
this.story.ins.save.set();
break;
case "capture":
this.system.getComponent(CVSetup).ins.saveState.set();
this.story.ins.save.set();
break;
case "download":
this.story.ins.download.set();
break;
default:
console.warn("Unhandled save method : ", event.detail.item.name);
}
}

protected onClickDownload()
Expand Down
Loading

0 comments on commit fa66165

Please sign in to comment.