UNPKG

sussudio

Version:

An unofficial VS Code Internal API

312 lines (311 loc) 12.8 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { addDisposableListener, EventHelper, EventType, reset, trackFocus } from "../../dom.mjs"; import { StandardKeyboardEvent } from "../../keyboardEvent.mjs"; import { EventType as TouchEventType, Gesture } from "../../touch.mjs"; import { renderLabelWithIcons } from "../iconLabel/iconLabels.mjs"; import { Action } from "../../../common/actions.mjs"; import { Codicon, CSSIcon } from "../../../common/codicons.mjs"; import { Color } from "../../../common/color.mjs"; import { Emitter } from "../../../common/event.mjs"; import { Disposable } from "../../../common/lifecycle.mjs"; import { localize } from "../../../../nls.mjs"; import "../../../../css!./button.mjs"; 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 { _element; options; _onDidClick = this._register(new Emitter()); get onDidClick() { return this._onDidClick.event; } focusTracker; constructor(container, options) { super(); this.options = options; this._element = document.createElement('a'); this._element.classList.add('monaco-button'); this._element.tabIndex = 0; this._element.setAttribute('role', 'button'); 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 || ''; 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._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); } })); } 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) { this._element.classList.add('monaco-text-button'); if (this.options.supportIcons) { const content = []; for (let segment of renderLabelWithIcons(value)) { 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; content.push(node); } else { content.push(segment); } } reset(this._element, ...content); } else { this._element.textContent = value; } if (typeof this.options.title === 'string') { this._element.title = this.options.title; } else if (this.options.title) { this._element.title = value; } } set icon(icon) { this._element.classList.add(...CSSIcon.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'); } focus() { this._element.focus(); } hasFocus() { return this._element === document.activeElement; } } export class ButtonWithDropdown extends Disposable { button; action; dropdownButton; separatorContainer; separator; element; _onDidClick = this._register(new Emitter()); onDidClick = this._onDidClick.event; constructor(container, options) { super(); this.element = document.createElement('div'); this.element.classList.add('monaco-button-dropdown'); container.appendChild(this.element); this.button = this._register(new Button(this.element, options)); this._register(this.button.onDidClick(e => this._onDidClick.fire(e))); this.action = this._register(new Action('primaryAction', this.button.label, undefined, true, async () => this._onDidClick.fire(undefined))); this.separatorContainer = document.createElement('div'); this.separatorContainer.classList.add('monaco-button-dropdown-separator'); this.separator = document.createElement('div'); this.separatorContainer.appendChild(this.separator); this.element.appendChild(this.separatorContainer); // Separator styles const border = options.buttonBorder; if (border) { this.separatorContainer.style.borderTop = '1px solid ' + border; this.separatorContainer.style.borderBottom = '1px solid ' + border; } const buttonBackground = options.secondary ? options.buttonSecondaryBackground : options.buttonBackground; this.separatorContainer.style.backgroundColor = buttonBackground ?? ''; this.separator.style.backgroundColor = options.buttonSeparator ?? ''; this.dropdownButton = this._register(new Button(this.element, { ...options, title: false, supportIcons: true })); this.dropdownButton.element.title = localize("button dropdown more actions", 'More Actions...'); this.dropdownButton.element.setAttribute('aria-haspopup', 'true'); this.dropdownButton.element.setAttribute('aria-expanded', 'false'); this.dropdownButton.element.classList.add('monaco-dropdown-button'); this.dropdownButton.icon = Codicon.dropDownButton; this._register(this.dropdownButton.onDidClick(e => { options.contextMenuProvider.showContextMenu({ getAnchor: () => this.dropdownButton.element, getActions: () => options.addPrimaryActionToDropdown === false ? [...options.actions] : [this.action, ...options.actions], actionRunner: options.actionRunner, onHide: () => this.dropdownButton.element.setAttribute('aria-expanded', 'false') }); this.dropdownButton.element.setAttribute('aria-expanded', 'true'); })); } set label(value) { this.button.label = value; this.action.label = value; } set icon(icon) { this.button.icon = icon; } set enabled(enabled) { this.button.enabled = enabled; this.dropdownButton.enabled = enabled; this.element.classList.toggle('disabled', !enabled); } get enabled() { return this.button.enabled; } focus() { this.button.focus(); } hasFocus() { return this.button.hasFocus() || this.dropdownButton.hasFocus(); } } export class ButtonWithDescription extends Button { _labelElement; _descriptionElement; constructor(container, options) { super(container, options); this._element.classList.add('monaco-description-button'); this._labelElement = document.createElement('div'); this._labelElement.classList.add('monaco-button-label'); this._element.appendChild(this._labelElement); this._descriptionElement = document.createElement('div'); this._descriptionElement.classList.add('monaco-button-description'); this._element.appendChild(this._descriptionElement); } set label(value) { this._element.classList.add('monaco-text-button'); if (this.options.supportIcons) { reset(this._labelElement, ...renderLabelWithIcons(value)); } else { this._labelElement.textContent = value; } if (typeof this.options.title === 'string') { this._element.title = this.options.title; } else if (this.options.title) { this._element.title = value; } } set description(value) { if (this.options.supportIcons) { reset(this._descriptionElement, ...renderLabelWithIcons(value)); } else { this._descriptionElement.textContent = value; } } } export class ButtonBar extends Disposable { container; _buttons = []; constructor(container) { super(); this.container = container; } get buttons() { return this._buttons; } addButton(options) { const button = this._register(new Button(this.container, options)); this.pushButton(button); return button; } addButtonWithDescription(options) { const button = this._register(new ButtonWithDescription(this.container, options)); this.pushButton(button); return button; } addButtonWithDropdown(options) { const button = this._register(new ButtonWithDropdown(this.container, options)); this.pushButton(button); return button; } pushButton(button) { this._buttons.push(button); const index = this._buttons.length - 1; this._register(addDisposableListener(button.element, EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); let eventHandled = true; // Next / Previous Button let buttonIndexToFocus; if (event.equals(15 /* KeyCode.LeftArrow */)) { buttonIndexToFocus = index > 0 ? index - 1 : this._buttons.length - 1; } else if (event.equals(17 /* KeyCode.RightArrow */)) { buttonIndexToFocus = index === this._buttons.length - 1 ? 0 : index + 1; } else { eventHandled = false; } if (eventHandled && typeof buttonIndexToFocus === 'number') { this._buttons[buttonIndexToFocus].focus(); EventHelper.stop(e, true); } })); } }