import { LitElementWw } from "@webwriter/lit"; import { html, TemplateResult } from "lit"; import { property } from "lit/decorators.js"; import { consume } from "@lit/context"; import { localized, msg } from "@lit/localize"; import { permissionsContext } from "utils/context"; import WwKbd from "components/ui/ww-kbd"; import { AnimationStatusType, PermissionsType } from "types"; import styles from "./top_bar.styles"; import SlButtonGroup from "@shoelace-style/shoelace/dist/components/button-group/button-group.component.js"; import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js"; import SlIcon from "@shoelace-style/shoelace/dist/components/icon/icon.component.js"; import SlTooltip from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.component.js"; import edit from "@tabler/icons/outline/edit.svg"; import algorithm from "@tabler/icons/outline/code-asterisk.svg"; import animation from "@tabler/icons/outline/keyframes.svg"; import play from "@tabler/icons/outline/player-play.svg"; import pause from "@tabler/icons/outline/player-pause.svg"; import stop from "@tabler/icons/outline/player-stop.svg"; import circlePlus from "@tabler/icons/outline/circle-plus-2.svg"; import linkPlus from "@tabler/icons/outline/link-plus.svg"; @localized() export class TopBar extends LitElementWw { public static get scopedElements() { return { "sl-button-group": SlButtonGroup, "sl-button": SlButton, "sl-icon": SlIcon, "sl-tooltip": SlTooltip, "ww-kbd": WwKbd, }; } static styles = styles; @property({ type: String }) accessor mode: "edit" | "algorithm" | "animation" | null = null; @property({ type: String }) accessor animationStatus: AnimationStatusType = "STOP"; @property({ type: Boolean }) accessor addingEdge: boolean = false; @property({ type: Number }) accessor playbackRate: number = 1; @consume({ context: permissionsContext, subscribe: true }) accessor permissions: PermissionsType = null!; protected render(): TemplateResult<1> { return html` ${this.permissions?.edit?.enabled !== false ? this.ModeButton( "edit", edit, html`${msg("Edit the graph")}`, ) : null} ${this.permissions?.algorithm?.enabled !== false ? this.ModeButton( "algorithm", algorithm, html`${msg("Execute an algorithm on the graph")}`, ) : null} ${this.permissions?.animation?.enabled !== false ? this.ModeButton( "animation", animation, html`${msg( "Create or customize the algorithm animation", )}`, ) : null}
${this.renderCycleButton()} ${this.renderActionButtons()}
`; } private renderActionButtons() { if (this.mode === "edit") { return [ this.permissions?.edit?.addNode !== false ? this.ActionButton( msg("Add node"), "add-node", circlePlus, html`${msg("Add a node to the graph")}`, ) : null, this.permissions?.edit?.addEdge !== false ? this.ActionButton( msg("Add edge"), "add-edge", linkPlus, html`${msg("Add an edge to the graph")}`, false, this.addingEdge ? "primary" : "default", ) : null, ]; } const actionButtons = []; if (this.permissions?.general?.play !== false) { if (this.animationStatus !== "RUN") { actionButtons.push( this.ActionButton( this.mode === "algorithm" ? msg("Execute") : msg("Play"), this.mode === "algorithm" ? "execute-algorithm" : "start-animation", play, this.mode === "algorithm" ? html`${msg("Execute the selected algorithm")}` : html`${msg("Play the animation")}`, false, "success", ), ); } else { actionButtons.push( this.ActionButton( msg("Pause"), "pause-animation", pause, html`${msg("Pause the animation")}`, false, ), ); } } if (this.animationStatus !== "STOP") { actionButtons.push( this.ActionButton( msg("Stop"), "stop-animation", stop, html`${msg("Stop the animation")}`, false, "danger", ), ); } return actionButtons; } private renderCycleButton() { if ( this.mode !== "edit" && this.permissions?.general?.playbackRate !== false && this.permissions?.general?.play !== false ) { return this.PlaybackRateButton(); } return null; } private ModeButton(mode: string, icon: string, tooltip: TemplateResult) { return html` ${tooltip} { this.onModeChange(mode); }} > ${mode === "edit" ? msg("Edit") : mode === "algorithm" ? msg("Algorithm") : msg("Animation")} `; } private ActionButton( action: string, event: string, icon: string, tooltip: TemplateResult, disabled: boolean = false, variant: | "default" | "primary" | "danger" | "success" | "warning" = "default", outline: boolean = false, ) { return html` ${tooltip} { this.dispatchEvent( new CustomEvent(event, { bubbles: true, composed: true, }), ); }} > ${action} `; } private PlaybackRateButton() { const values = [0.5, 1, 1.5, 2]; return html` { const currentIndex = values.indexOf(this.playbackRate); const nextIndex = (currentIndex + 1) % values.length; this.dispatchEvent( new CustomEvent("playback-rate-change", { bubbles: true, composed: true, detail: { playbackRate: values[nextIndex] }, }), ); }} > ${this.playbackRate}x `; } private onModeChange(mode: string) { this.mode = mode as "edit" | "algorithm" | "animation"; const event = new CustomEvent("mode-change", { bubbles: true, composed: true, detail: { mode: this.mode }, }); this.dispatchEvent(event); } }