156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
import 'vs/css!./bannerController';
|
|
import { $, append, clearNode } from 'vs/base/browser/dom';
|
|
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
|
import { Action } from 'vs/base/common/actions';
|
|
import { MarkdownString } from 'vs/base/common/htmlContent';
|
|
import { Disposable } from 'vs/base/common/lifecycle';
|
|
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
|
|
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
|
import { ILinkDescriptor, Link } from 'vs/platform/opener/browser/link';
|
|
import { widgetClose } from 'vs/platform/theme/common/iconRegistry';
|
|
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
|
|
|
const BANNER_ELEMENT_HEIGHT = 26;
|
|
|
|
export class BannerController extends Disposable {
|
|
private readonly banner: Banner;
|
|
|
|
constructor(
|
|
private readonly _editor: ICodeEditor,
|
|
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
|
) {
|
|
super();
|
|
|
|
this.banner = this._register(this.instantiationService.createInstance(Banner));
|
|
}
|
|
|
|
public hide() {
|
|
this._editor.setBanner(null, 0);
|
|
this.banner.clear();
|
|
}
|
|
|
|
public show(item: IBannerItem) {
|
|
this.banner.show({
|
|
...item,
|
|
onClose: () => {
|
|
this.hide();
|
|
if (item.onClose) {
|
|
item.onClose();
|
|
}
|
|
}
|
|
});
|
|
this._editor.setBanner(this.banner.element, BANNER_ELEMENT_HEIGHT);
|
|
}
|
|
}
|
|
|
|
// TODO@hediet: Investigate if this can be reused by the workspace banner (bannerPart.ts).
|
|
class Banner extends Disposable {
|
|
public element: HTMLElement;
|
|
|
|
private readonly markdownRenderer: MarkdownRenderer;
|
|
|
|
private messageActionsContainer: HTMLElement | undefined;
|
|
|
|
private actionBar: ActionBar | undefined;
|
|
|
|
constructor(
|
|
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
|
) {
|
|
super();
|
|
|
|
this.markdownRenderer = this.instantiationService.createInstance(MarkdownRenderer, {});
|
|
|
|
this.element = $('div.editor-banner');
|
|
this.element.tabIndex = 0;
|
|
}
|
|
|
|
private getAriaLabel(item: IBannerItem): string | undefined {
|
|
if (item.ariaLabel) {
|
|
return item.ariaLabel;
|
|
}
|
|
if (typeof item.message === 'string') {
|
|
return item.message;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
private getBannerMessage(message: MarkdownString | string): HTMLElement {
|
|
if (typeof message === 'string') {
|
|
const element = $('span');
|
|
element.innerText = message;
|
|
return element;
|
|
}
|
|
|
|
return this.markdownRenderer.render(message).element;
|
|
}
|
|
|
|
public clear() {
|
|
clearNode(this.element);
|
|
}
|
|
|
|
public show(item: IBannerItem) {
|
|
// Clear previous item
|
|
clearNode(this.element);
|
|
|
|
// Banner aria label
|
|
const ariaLabel = this.getAriaLabel(item);
|
|
if (ariaLabel) {
|
|
this.element.setAttribute('aria-label', ariaLabel);
|
|
}
|
|
|
|
// Icon
|
|
const iconContainer = append(this.element, $('div.icon-container'));
|
|
iconContainer.setAttribute('aria-hidden', 'true');
|
|
|
|
if (item.icon) {
|
|
iconContainer.appendChild($(`div${ThemeIcon.asCSSSelector(item.icon)}`));
|
|
}
|
|
|
|
// Message
|
|
const messageContainer = append(this.element, $('div.message-container'));
|
|
messageContainer.setAttribute('aria-hidden', 'true');
|
|
messageContainer.appendChild(this.getBannerMessage(item.message));
|
|
|
|
// Message Actions
|
|
this.messageActionsContainer = append(this.element, $('div.message-actions-container'));
|
|
if (item.actions) {
|
|
for (const action of item.actions) {
|
|
this._register(this.instantiationService.createInstance(Link, this.messageActionsContainer, { ...action, tabIndex: -1 }, {}));
|
|
}
|
|
}
|
|
|
|
// Action
|
|
const actionBarContainer = append(this.element, $('div.action-container'));
|
|
this.actionBar = this._register(new ActionBar(actionBarContainer));
|
|
this.actionBar.push(this._register(
|
|
new Action(
|
|
'banner.close',
|
|
'Close Banner',
|
|
ThemeIcon.asClassName(widgetClose),
|
|
true,
|
|
() => {
|
|
if (typeof item.onClose === 'function') {
|
|
item.onClose();
|
|
}
|
|
}
|
|
)
|
|
), { icon: true, label: false });
|
|
this.actionBar.setFocusable(false);
|
|
}
|
|
}
|
|
|
|
export interface IBannerItem {
|
|
readonly id: string;
|
|
readonly icon: ThemeIcon | undefined;
|
|
readonly message: string | MarkdownString;
|
|
readonly actions?: ILinkDescriptor[];
|
|
readonly ariaLabel?: string;
|
|
readonly onClose?: () => void;
|
|
}
|