/******************************************************************************** * Copyright (C) 2017-2018 TypeFox and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ import * as React from 'react'; import { inject, injectable } from 'inversify'; import Octicon, { getIconByName } from './icons'; import { CommandService } from '@gedit/command'; import { ReactWidget } from '../widgets/react-widget'; import { FrontendApplicationStateService } from '@gedit/application-common/lib/browser'; import { LabelIcon, LabelParser } from '@gedit/theme'; export interface StatusBarEntry { /** * For icons we use octicons and fontawesome icons. octicons take precedence over fontawesome. Get more information and the class names * here: http://fontawesome.io/icons/ * To set a text with icon use the following pattern in text string: * $(fontawesomeClassName) * To use animated icons use the following pattern: * $(fontawesomeClassName~typeOfAnimation) * Type of animation can be either spin or pulse. * Look here for more information to animated icons: * http://fontawesome.io/examples/#animated */ text: string; alignment: StatusBarAlignment; color?: string; className?: string; tooltip?: string; command?: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any arguments?: any[]; priority?: number; onclick?: (e: MouseEvent) => void; } export enum StatusBarAlignment { LEFT, RIGHT } export interface StatusBarEntryAttributes { className?: string; title?: string; style?: object; onClick?: (e: MouseEvent) => void; } export const STATUSBAR_WIDGET_FACTORY_ID = 'statusBar'; export const StatusBar = Symbol('StatusBar'); export interface StatusBar { setBackgroundColor(color?: string): Promise; setColor(color?: string): Promise; setElement(id: string, entry: StatusBarEntry): Promise; removeElement(id: string): Promise; hide(): void; show(): void; } export namespace StatusBar { export interface Options { hide?: boolean } } @injectable() export class StatusBarImpl extends ReactWidget implements StatusBar { protected backgroundColor: string | undefined; protected color: string | undefined; protected entries: Map = new Map(); constructor( @inject(CommandService) protected readonly commands: CommandService, @inject(LabelParser) protected readonly entryService: LabelParser, @inject(FrontendApplicationStateService) protected readonly applicationStateService: FrontendApplicationStateService ) { super(); delete this.scrollOptions; this.id = 'theia-statusBar'; this.addClass('noselect'); } hide(): void { this.addClass('p-mod-hidden'); } show(): void { this.removeClass('p-mod-hidden'); } protected get ready(): Promise { return this.applicationStateService.reachedAnyState('initialized_layout', 'ready'); } async setElement(id: string, entry: StatusBarEntry): Promise { await this.ready; this.entries.set(id, entry); this.update(); } async removeElement(id: string): Promise { await this.ready; this.entries.delete(id); this.update(); } async setBackgroundColor(color?: string): Promise { await this.ready; this.internalSetBackgroundColor(color); this.update(); } async setColor(color?: string): Promise { await this.ready; this.internalSetColor(color); this.update(); } protected internalSetBackgroundColor(color?: string): void { this.backgroundColor = color; this.node.style.backgroundColor = this.backgroundColor || ''; } protected internalSetColor(color?: string): void { this.color = color; } protected render(): React.JSX.Element { const leftEntries: React.JSX.Element[] = []; const rightEntries: React.JSX.Element[] = []; const elements = Array.from(this.entries).sort((left, right) => { const lp = left[1].priority || 0; const rp = right[1].priority || 0; return rp - lp; }); elements.forEach(([id, entry]) => { if (entry.alignment === StatusBarAlignment.LEFT) { leftEntries.push(this.renderElement(id, entry)); } else { rightEntries.push(this.renderElement(id, entry)); } }); return
{leftEntries}
{rightEntries}
; } protected onclick(entry: StatusBarEntry): () => void { return () => { if (entry.command) { const args = entry.arguments || []; this.commands.executeCommand(entry.command, ...args); } }; } protected createAttributes(entry: StatusBarEntry): StatusBarEntryAttributes { const attrs: StatusBarEntryAttributes = {}; if (entry.command) { attrs.onClick = this.onclick(entry); attrs.className = 'element hasCommand'; } else if (entry.onclick) { attrs.onClick = e => { if (entry.onclick) { entry.onclick(e); } }; attrs.className = 'element hasCommand'; } else { attrs.className = 'element'; } if (entry.tooltip) { attrs.title = entry.tooltip; } attrs.style = { color: entry.color || this.color }; if (entry.className) { attrs.className += ' ' + entry.className; } return attrs; } protected renderElement(id: string, entry: StatusBarEntry): React.JSX.Element { const childStrings = this.entryService.parse(entry.text); const children: React.JSX.Element[] = []; childStrings.forEach((val, key) => { if (!(typeof val === 'string') && LabelIcon.is(val)) { const octicon = getIconByName(val.name); if (octicon) { children.push(); } else { children.push(); } } else { children.push({val}); } }); const elementInnerDiv = {children}; return React.createElement('div', {key: id, ...this.createAttributes(entry)}, elementInnerDiv); } }