monaco-editor-core
Version:
A browser based code editor
216 lines (215 loc) • 8.99 kB
JavaScript
import { addDisposableListener, EventHelper, EventType, reset, trackFocus } from '../../dom.js';
import { sanitize } from '../../dompurify/dompurify.js';
import { StandardKeyboardEvent } from '../../keyboardEvent.js';
import { renderMarkdown, renderStringAsPlaintext } from '../../markdownRenderer.js';
import { Gesture, EventType as TouchEventType } from '../../touch.js';
import { getDefaultHoverDelegate } from '../hover/hoverDelegateFactory.js';
import { renderLabelWithIcons } from '../iconLabel/iconLabels.js';
import { Color } from '../../../common/color.js';
import { Emitter } from '../../../common/event.js';
import { isMarkdownString, markdownStringEqual } from '../../../common/htmlContent.js';
import { Disposable } from '../../../common/lifecycle.js';
import { ThemeIcon } from '../../../common/themables.js';
import './button.css';
import { getBaseLayerHoverDelegate } from '../hover/hoverDelegate2.js';
export const unthemedButtonStyles = {
buttonBackground: '#0E639C',
buttonHoverBackground: '#006BB3',
buttonSeparator: Color.white.toString(),
buttonForeground: Color.white.toString(),
buttonBorder: undefined,
buttonSecondaryBackground: undefined,
buttonSecondaryForeground: undefined,
buttonSecondaryHoverBackground: undefined
};
export class Button extends Disposable {
get onDidClick() { return this._onDidClick.event; }
constructor(container, options) {
super();
this._label = '';
this._onDidClick = this._register(new Emitter());
this._onDidEscape = this._register(new Emitter());
this.options = options;
this._element = document.createElement('a');
this._element.classList.add('monaco-button');
this._element.tabIndex = 0;
this._element.setAttribute('role', 'button');
this._element.classList.toggle('secondary', !!options.secondary);
const background = options.secondary ? options.buttonSecondaryBackground : options.buttonBackground;
const foreground = options.secondary ? options.buttonSecondaryForeground : options.buttonForeground;
this._element.style.color = foreground || '';
this._element.style.backgroundColor = background || '';
if (options.supportShortLabel) {
this._labelShortElement = document.createElement('div');
this._labelShortElement.classList.add('monaco-button-label-short');
this._element.appendChild(this._labelShortElement);
this._labelElement = document.createElement('div');
this._labelElement.classList.add('monaco-button-label');
this._element.appendChild(this._labelElement);
this._element.classList.add('monaco-text-button-with-short-label');
}
if (typeof options.title === 'string') {
this.setTitle(options.title);
}
if (typeof options.ariaLabel === 'string') {
this._element.setAttribute('aria-label', options.ariaLabel);
}
container.appendChild(this._element);
this._register(Gesture.addTarget(this._element));
[EventType.CLICK, TouchEventType.Tap].forEach(eventType => {
this._register(addDisposableListener(this._element, eventType, e => {
if (!this.enabled) {
EventHelper.stop(e);
return;
}
this._onDidClick.fire(e);
}));
});
this._register(addDisposableListener(this._element, EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
let eventHandled = false;
if (this.enabled && (event.equals(3 /* KeyCode.Enter */) || event.equals(10 /* KeyCode.Space */))) {
this._onDidClick.fire(e);
eventHandled = true;
}
else if (event.equals(9 /* KeyCode.Escape */)) {
this._onDidEscape.fire(e);
this._element.blur();
eventHandled = true;
}
if (eventHandled) {
EventHelper.stop(event, true);
}
}));
this._register(addDisposableListener(this._element, EventType.MOUSE_OVER, e => {
if (!this._element.classList.contains('disabled')) {
this.updateBackground(true);
}
}));
this._register(addDisposableListener(this._element, EventType.MOUSE_OUT, e => {
this.updateBackground(false); // restore standard styles
}));
// Also set hover background when button is focused for feedback
this.focusTracker = this._register(trackFocus(this._element));
this._register(this.focusTracker.onDidFocus(() => { if (this.enabled) {
this.updateBackground(true);
} }));
this._register(this.focusTracker.onDidBlur(() => { if (this.enabled) {
this.updateBackground(false);
} }));
}
dispose() {
super.dispose();
this._element.remove();
}
getContentElements(content) {
const elements = [];
for (let segment of renderLabelWithIcons(content)) {
if (typeof (segment) === 'string') {
segment = segment.trim();
// Ignore empty segment
if (segment === '') {
continue;
}
// Convert string segments to <span> nodes
const node = document.createElement('span');
node.textContent = segment;
elements.push(node);
}
else {
elements.push(segment);
}
}
return elements;
}
updateBackground(hover) {
let background;
if (this.options.secondary) {
background = hover ? this.options.buttonSecondaryHoverBackground : this.options.buttonSecondaryBackground;
}
else {
background = hover ? this.options.buttonHoverBackground : this.options.buttonBackground;
}
if (background) {
this._element.style.backgroundColor = background;
}
}
get element() {
return this._element;
}
set label(value) {
if (this._label === value) {
return;
}
if (isMarkdownString(this._label) && isMarkdownString(value) && markdownStringEqual(this._label, value)) {
return;
}
this._element.classList.add('monaco-text-button');
const labelElement = this.options.supportShortLabel ? this._labelElement : this._element;
if (isMarkdownString(value)) {
const rendered = renderMarkdown(value, { inline: true });
rendered.dispose();
// Don't include outer `<p>`
const root = rendered.element.querySelector('p')?.innerHTML;
if (root) {
// Only allow a very limited set of inline html tags
const sanitized = sanitize(root, { ADD_TAGS: ['b', 'i', 'u', 'code', 'span'], ALLOWED_ATTR: ['class'], RETURN_TRUSTED_TYPE: true });
labelElement.innerHTML = sanitized;
}
else {
reset(labelElement);
}
}
else {
if (this.options.supportIcons) {
reset(labelElement, ...this.getContentElements(value));
}
else {
labelElement.textContent = value;
}
}
let title = '';
if (typeof this.options.title === 'string') {
title = this.options.title;
}
else if (this.options.title) {
title = renderStringAsPlaintext(value);
}
this.setTitle(title);
if (typeof this.options.ariaLabel === 'string') {
this._element.setAttribute('aria-label', this.options.ariaLabel);
}
else if (this.options.ariaLabel) {
this._element.setAttribute('aria-label', title);
}
this._label = value;
}
get label() {
return this._label;
}
set icon(icon) {
this._element.classList.add(...ThemeIcon.asClassNameArray(icon));
}
set enabled(value) {
if (value) {
this._element.classList.remove('disabled');
this._element.setAttribute('aria-disabled', String(false));
this._element.tabIndex = 0;
}
else {
this._element.classList.add('disabled');
this._element.setAttribute('aria-disabled', String(true));
}
}
get enabled() {
return !this._element.classList.contains('disabled');
}
setTitle(title) {
if (!this._hover && title !== '') {
this._hover = this._register(getBaseLayerHoverDelegate().setupManagedHover(this.options.hoverDelegate ?? getDefaultHoverDelegate('mouse'), this._element, title));
}
else if (this._hover) {
this._hover.update(title);
}
}
}