UNPKG

monaco-editor-core

Version:

A browser based code editor

216 lines (215 loc) • 8.99 kB
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); } } }