feat: 添加vscode编辑器
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { isFirefox } from '../../browser.js';
|
||||
import { DataTransfers } from '../../dnd.js';
|
||||
import { $, addDisposableListener, append, EventHelper, EventType } from '../../dom.js';
|
||||
import { EventType as TouchEventType, Gesture } from '../../touch.js';
|
||||
import { setupCustomHover } from '../iconLabel/iconLabelHover.js';
|
||||
import { Action, ActionRunner, Separator } from '../../../common/actions.js';
|
||||
import { Disposable } from '../../../common/lifecycle.js';
|
||||
import * as platform from '../../../common/platform.js';
|
||||
import * as types from '../../../common/types.js';
|
||||
import './actionbar.css';
|
||||
import * as nls from '../../../../nls.js';
|
||||
export class BaseActionViewItem extends Disposable {
|
||||
constructor(context, action, options = {}) {
|
||||
super();
|
||||
this.options = options;
|
||||
this._context = context || this;
|
||||
this._action = action;
|
||||
if (action instanceof Action) {
|
||||
this._register(action.onDidChange(event => {
|
||||
if (!this.element) {
|
||||
// we have not been rendered yet, so there
|
||||
// is no point in updating the UI
|
||||
return;
|
||||
}
|
||||
this.handleActionChangeEvent(event);
|
||||
}));
|
||||
}
|
||||
}
|
||||
get action() {
|
||||
return this._action;
|
||||
}
|
||||
handleActionChangeEvent(event) {
|
||||
if (event.enabled !== undefined) {
|
||||
this.updateEnabled();
|
||||
}
|
||||
if (event.checked !== undefined) {
|
||||
this.updateChecked();
|
||||
}
|
||||
if (event.class !== undefined) {
|
||||
this.updateClass();
|
||||
}
|
||||
if (event.label !== undefined) {
|
||||
this.updateLabel();
|
||||
this.updateTooltip();
|
||||
}
|
||||
if (event.tooltip !== undefined) {
|
||||
this.updateTooltip();
|
||||
}
|
||||
}
|
||||
get actionRunner() {
|
||||
if (!this._actionRunner) {
|
||||
this._actionRunner = this._register(new ActionRunner());
|
||||
}
|
||||
return this._actionRunner;
|
||||
}
|
||||
set actionRunner(actionRunner) {
|
||||
this._actionRunner = actionRunner;
|
||||
}
|
||||
getAction() {
|
||||
return this._action;
|
||||
}
|
||||
isEnabled() {
|
||||
return this._action.enabled;
|
||||
}
|
||||
setActionContext(newContext) {
|
||||
this._context = newContext;
|
||||
}
|
||||
render(container) {
|
||||
const element = this.element = container;
|
||||
this._register(Gesture.addTarget(container));
|
||||
const enableDragging = this.options && this.options.draggable;
|
||||
if (enableDragging) {
|
||||
container.draggable = true;
|
||||
if (isFirefox) {
|
||||
// Firefox: requires to set a text data transfer to get going
|
||||
this._register(addDisposableListener(container, EventType.DRAG_START, e => { var _a; return (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.setData(DataTransfers.TEXT, this._action.label); }));
|
||||
}
|
||||
}
|
||||
this._register(addDisposableListener(element, TouchEventType.Tap, e => this.onClick(e, true))); // Preserve focus on tap #125470
|
||||
this._register(addDisposableListener(element, EventType.MOUSE_DOWN, e => {
|
||||
if (!enableDragging) {
|
||||
EventHelper.stop(e, true); // do not run when dragging is on because that would disable it
|
||||
}
|
||||
if (this._action.enabled && e.button === 0) {
|
||||
element.classList.add('active');
|
||||
}
|
||||
}));
|
||||
if (platform.isMacintosh) {
|
||||
// macOS: allow to trigger the button when holding Ctrl+key and pressing the
|
||||
// main mouse button. This is for scenarios where e.g. some interaction forces
|
||||
// the Ctrl+key to be pressed and hold but the user still wants to interact
|
||||
// with the actions (for example quick access in quick navigation mode).
|
||||
this._register(addDisposableListener(element, EventType.CONTEXT_MENU, e => {
|
||||
if (e.button === 0 && e.ctrlKey === true) {
|
||||
this.onClick(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
this._register(addDisposableListener(element, EventType.CLICK, e => {
|
||||
EventHelper.stop(e, true);
|
||||
// menus do not use the click event
|
||||
if (!(this.options && this.options.isMenu)) {
|
||||
this.onClick(e);
|
||||
}
|
||||
}));
|
||||
this._register(addDisposableListener(element, EventType.DBLCLICK, e => {
|
||||
EventHelper.stop(e, true);
|
||||
}));
|
||||
[EventType.MOUSE_UP, EventType.MOUSE_OUT].forEach(event => {
|
||||
this._register(addDisposableListener(element, event, e => {
|
||||
EventHelper.stop(e);
|
||||
element.classList.remove('active');
|
||||
}));
|
||||
});
|
||||
}
|
||||
onClick(event, preserveFocus = false) {
|
||||
var _a;
|
||||
EventHelper.stop(event, true);
|
||||
const context = types.isUndefinedOrNull(this._context) ? ((_a = this.options) === null || _a === void 0 ? void 0 : _a.useEventAsContext) ? event : { preserveFocus } : this._context;
|
||||
this.actionRunner.run(this._action, context);
|
||||
}
|
||||
// Only set the tabIndex on the element once it is about to get focused
|
||||
// That way this element wont be a tab stop when it is not needed #106441
|
||||
focus() {
|
||||
if (this.element) {
|
||||
this.element.tabIndex = 0;
|
||||
this.element.focus();
|
||||
this.element.classList.add('focused');
|
||||
}
|
||||
}
|
||||
blur() {
|
||||
if (this.element) {
|
||||
this.element.blur();
|
||||
this.element.tabIndex = -1;
|
||||
this.element.classList.remove('focused');
|
||||
}
|
||||
}
|
||||
setFocusable(focusable) {
|
||||
if (this.element) {
|
||||
this.element.tabIndex = focusable ? 0 : -1;
|
||||
}
|
||||
}
|
||||
get trapsArrowNavigation() {
|
||||
return false;
|
||||
}
|
||||
updateEnabled() {
|
||||
// implement in subclass
|
||||
}
|
||||
updateLabel() {
|
||||
// implement in subclass
|
||||
}
|
||||
getTooltip() {
|
||||
return this.getAction().tooltip;
|
||||
}
|
||||
updateTooltip() {
|
||||
var _a;
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
const title = (_a = this.getTooltip()) !== null && _a !== void 0 ? _a : '';
|
||||
this.element.setAttribute('aria-label', title);
|
||||
if (!this.options.hoverDelegate) {
|
||||
this.element.title = title;
|
||||
}
|
||||
else {
|
||||
this.element.title = '';
|
||||
if (!this.customHover) {
|
||||
this.customHover = setupCustomHover(this.options.hoverDelegate, this.element, title);
|
||||
this._store.add(this.customHover);
|
||||
}
|
||||
else {
|
||||
this.customHover.update(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateClass() {
|
||||
// implement in subclass
|
||||
}
|
||||
updateChecked() {
|
||||
// implement in subclass
|
||||
}
|
||||
dispose() {
|
||||
if (this.element) {
|
||||
this.element.remove();
|
||||
this.element = undefined;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
export class ActionViewItem extends BaseActionViewItem {
|
||||
constructor(context, action, options = {}) {
|
||||
super(context, action, options);
|
||||
this.options = options;
|
||||
this.options.icon = options.icon !== undefined ? options.icon : false;
|
||||
this.options.label = options.label !== undefined ? options.label : true;
|
||||
this.cssClass = '';
|
||||
}
|
||||
render(container) {
|
||||
super.render(container);
|
||||
if (this.element) {
|
||||
this.label = append(this.element, $('a.action-label'));
|
||||
}
|
||||
if (this.label) {
|
||||
if (this._action.id === Separator.ID) {
|
||||
this.label.setAttribute('role', 'presentation'); // A separator is a presentation item
|
||||
}
|
||||
else {
|
||||
if (this.options.isMenu) {
|
||||
this.label.setAttribute('role', 'menuitem');
|
||||
}
|
||||
else {
|
||||
this.label.setAttribute('role', 'button');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.options.label && this.options.keybinding && this.element) {
|
||||
append(this.element, $('span.keybinding')).textContent = this.options.keybinding;
|
||||
}
|
||||
this.updateClass();
|
||||
this.updateLabel();
|
||||
this.updateTooltip();
|
||||
this.updateEnabled();
|
||||
this.updateChecked();
|
||||
}
|
||||
// Only set the tabIndex on the element once it is about to get focused
|
||||
// That way this element wont be a tab stop when it is not needed #106441
|
||||
focus() {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = 0;
|
||||
this.label.focus();
|
||||
}
|
||||
}
|
||||
blur() {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = -1;
|
||||
}
|
||||
}
|
||||
setFocusable(focusable) {
|
||||
if (this.label) {
|
||||
this.label.tabIndex = focusable ? 0 : -1;
|
||||
}
|
||||
}
|
||||
updateLabel() {
|
||||
if (this.options.label && this.label) {
|
||||
this.label.textContent = this.getAction().label;
|
||||
}
|
||||
}
|
||||
getTooltip() {
|
||||
let title = null;
|
||||
if (this.getAction().tooltip) {
|
||||
title = this.getAction().tooltip;
|
||||
}
|
||||
else if (!this.options.label && this.getAction().label && this.options.icon) {
|
||||
title = this.getAction().label;
|
||||
if (this.options.keybinding) {
|
||||
title = nls.localize({ key: 'titleLabel', comment: ['action title', 'action keybinding'] }, "{0} ({1})", title, this.options.keybinding);
|
||||
}
|
||||
}
|
||||
return title !== null && title !== void 0 ? title : undefined;
|
||||
}
|
||||
updateClass() {
|
||||
var _a;
|
||||
if (this.cssClass && this.label) {
|
||||
this.label.classList.remove(...this.cssClass.split(' '));
|
||||
}
|
||||
if (this.options.icon) {
|
||||
this.cssClass = this.getAction().class;
|
||||
if (this.label) {
|
||||
this.label.classList.add('codicon');
|
||||
if (this.cssClass) {
|
||||
this.label.classList.add(...this.cssClass.split(' '));
|
||||
}
|
||||
}
|
||||
this.updateEnabled();
|
||||
}
|
||||
else {
|
||||
(_a = this.label) === null || _a === void 0 ? void 0 : _a.classList.remove('codicon');
|
||||
}
|
||||
}
|
||||
updateEnabled() {
|
||||
var _a, _b;
|
||||
if (this.getAction().enabled) {
|
||||
if (this.label) {
|
||||
this.label.removeAttribute('aria-disabled');
|
||||
this.label.classList.remove('disabled');
|
||||
}
|
||||
(_a = this.element) === null || _a === void 0 ? void 0 : _a.classList.remove('disabled');
|
||||
}
|
||||
else {
|
||||
if (this.label) {
|
||||
this.label.setAttribute('aria-disabled', 'true');
|
||||
this.label.classList.add('disabled');
|
||||
}
|
||||
(_b = this.element) === null || _b === void 0 ? void 0 : _b.classList.add('disabled');
|
||||
}
|
||||
}
|
||||
updateChecked() {
|
||||
if (this.label) {
|
||||
if (this.getAction().checked) {
|
||||
this.label.classList.add('checked');
|
||||
}
|
||||
else {
|
||||
this.label.classList.remove('checked');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-action-bar {
|
||||
white-space: nowrap;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.monaco-action-bar .actions-container {
|
||||
display: flex;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-action-bar.vertical .actions-container {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item {
|
||||
display: block;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
position: relative; /* DO NOT REMOVE - this is the key to preventing the ghosting icon bug in Chrome 42 */
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item .icon,
|
||||
.monaco-action-bar .action-item .codicon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item .codicon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-label {
|
||||
font-size: 11px;
|
||||
padding: 3px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.disabled .action-label,
|
||||
.monaco-action-bar .action-item.disabled .action-label::before,
|
||||
.monaco-action-bar .action-item.disabled .action-label:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Vertical actions */
|
||||
|
||||
.monaco-action-bar.vertical {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.monaco-action-bar.vertical .action-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-action-bar.vertical .action-label.separator {
|
||||
display: block;
|
||||
border-bottom: 1px solid #bbb;
|
||||
padding-top: 1px;
|
||||
margin-left: .8em;
|
||||
margin-right: .8em;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item .action-label.separator {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
margin: 5px 4px !important;
|
||||
cursor: default;
|
||||
min-width: 1px;
|
||||
padding: 0;
|
||||
background-color: #bbb;
|
||||
}
|
||||
|
||||
.secondary-actions .monaco-action-bar .action-label {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* Action Items */
|
||||
.monaco-action-bar .action-item.select-container {
|
||||
overflow: hidden; /* somehow the dropdown overflows its container, we prevent it here to not push */
|
||||
flex: 1;
|
||||
max-width: 170px;
|
||||
min-width: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.action-dropdown-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-action-bar .action-item.action-dropdown-item > .action-label {
|
||||
margin-right: 1px;
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
import * as DOM from '../../dom.js';
|
||||
import { StandardKeyboardEvent } from '../../keyboardEvent.js';
|
||||
import { ActionViewItem, BaseActionViewItem } from './actionViewItems.js';
|
||||
import { ActionRunner, Separator } from '../../../common/actions.js';
|
||||
import { Emitter } from '../../../common/event.js';
|
||||
import { Disposable, dispose } from '../../../common/lifecycle.js';
|
||||
import * as types from '../../../common/types.js';
|
||||
import './actionbar.css';
|
||||
export class ActionBar extends Disposable {
|
||||
constructor(container, options = {}) {
|
||||
var _a, _b, _c, _d, _e, _f;
|
||||
super();
|
||||
// Trigger Key Tracking
|
||||
this.triggerKeyDown = false;
|
||||
this.focusable = true;
|
||||
this._onDidBlur = this._register(new Emitter());
|
||||
this.onDidBlur = this._onDidBlur.event;
|
||||
this._onDidCancel = this._register(new Emitter({ onFirstListenerAdd: () => this.cancelHasListener = true }));
|
||||
this.onDidCancel = this._onDidCancel.event;
|
||||
this.cancelHasListener = false;
|
||||
this._onDidRun = this._register(new Emitter());
|
||||
this.onDidRun = this._onDidRun.event;
|
||||
this._onBeforeRun = this._register(new Emitter());
|
||||
this.onBeforeRun = this._onBeforeRun.event;
|
||||
this.options = options;
|
||||
this._context = (_a = options.context) !== null && _a !== void 0 ? _a : null;
|
||||
this._orientation = (_b = this.options.orientation) !== null && _b !== void 0 ? _b : 0 /* ActionsOrientation.HORIZONTAL */;
|
||||
this._triggerKeys = {
|
||||
keyDown: (_d = (_c = this.options.triggerKeys) === null || _c === void 0 ? void 0 : _c.keyDown) !== null && _d !== void 0 ? _d : false,
|
||||
keys: (_f = (_e = this.options.triggerKeys) === null || _e === void 0 ? void 0 : _e.keys) !== null && _f !== void 0 ? _f : [3 /* KeyCode.Enter */, 10 /* KeyCode.Space */]
|
||||
};
|
||||
if (this.options.actionRunner) {
|
||||
this._actionRunner = this.options.actionRunner;
|
||||
}
|
||||
else {
|
||||
this._actionRunner = new ActionRunner();
|
||||
this._register(this._actionRunner);
|
||||
}
|
||||
this._register(this._actionRunner.onDidRun(e => this._onDidRun.fire(e)));
|
||||
this._register(this._actionRunner.onBeforeRun(e => this._onBeforeRun.fire(e)));
|
||||
this._actionIds = [];
|
||||
this.viewItems = [];
|
||||
this.viewItemDisposables = new Map();
|
||||
this.focusedItem = undefined;
|
||||
this.domNode = document.createElement('div');
|
||||
this.domNode.className = 'monaco-action-bar';
|
||||
if (options.animated !== false) {
|
||||
this.domNode.classList.add('animated');
|
||||
}
|
||||
let previousKeys;
|
||||
let nextKeys;
|
||||
switch (this._orientation) {
|
||||
case 0 /* ActionsOrientation.HORIZONTAL */:
|
||||
previousKeys = [15 /* KeyCode.LeftArrow */];
|
||||
nextKeys = [17 /* KeyCode.RightArrow */];
|
||||
break;
|
||||
case 1 /* ActionsOrientation.VERTICAL */:
|
||||
previousKeys = [16 /* KeyCode.UpArrow */];
|
||||
nextKeys = [18 /* KeyCode.DownArrow */];
|
||||
this.domNode.className += ' vertical';
|
||||
break;
|
||||
}
|
||||
this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_DOWN, e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
let eventHandled = true;
|
||||
const focusedItem = typeof this.focusedItem === 'number' ? this.viewItems[this.focusedItem] : undefined;
|
||||
if (previousKeys && (event.equals(previousKeys[0]) || event.equals(previousKeys[1]))) {
|
||||
eventHandled = this.focusPrevious();
|
||||
}
|
||||
else if (nextKeys && (event.equals(nextKeys[0]) || event.equals(nextKeys[1]))) {
|
||||
eventHandled = this.focusNext();
|
||||
}
|
||||
else if (event.equals(9 /* KeyCode.Escape */) && this.cancelHasListener) {
|
||||
this._onDidCancel.fire();
|
||||
}
|
||||
else if (event.equals(14 /* KeyCode.Home */)) {
|
||||
eventHandled = this.focusFirst();
|
||||
}
|
||||
else if (event.equals(13 /* KeyCode.End */)) {
|
||||
eventHandled = this.focusLast();
|
||||
}
|
||||
else if (event.equals(2 /* KeyCode.Tab */) && focusedItem instanceof BaseActionViewItem && focusedItem.trapsArrowNavigation) {
|
||||
eventHandled = this.focusNext();
|
||||
}
|
||||
else if (this.isTriggerKeyEvent(event)) {
|
||||
// Staying out of the else branch even if not triggered
|
||||
if (this._triggerKeys.keyDown) {
|
||||
this.doTrigger(event);
|
||||
}
|
||||
else {
|
||||
this.triggerKeyDown = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
eventHandled = false;
|
||||
}
|
||||
if (eventHandled) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
}));
|
||||
this._register(DOM.addDisposableListener(this.domNode, DOM.EventType.KEY_UP, e => {
|
||||
const event = new StandardKeyboardEvent(e);
|
||||
// Run action on Enter/Space
|
||||
if (this.isTriggerKeyEvent(event)) {
|
||||
if (!this._triggerKeys.keyDown && this.triggerKeyDown) {
|
||||
this.triggerKeyDown = false;
|
||||
this.doTrigger(event);
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
// Recompute focused item
|
||||
else if (event.equals(2 /* KeyCode.Tab */) || event.equals(1024 /* KeyMod.Shift */ | 2 /* KeyCode.Tab */)) {
|
||||
this.updateFocusedItem();
|
||||
}
|
||||
}));
|
||||
this.focusTracker = this._register(DOM.trackFocus(this.domNode));
|
||||
this._register(this.focusTracker.onDidBlur(() => {
|
||||
if (DOM.getActiveElement() === this.domNode || !DOM.isAncestor(DOM.getActiveElement(), this.domNode)) {
|
||||
this._onDidBlur.fire();
|
||||
this.focusedItem = undefined;
|
||||
this.previouslyFocusedItem = undefined;
|
||||
this.triggerKeyDown = false;
|
||||
}
|
||||
}));
|
||||
this._register(this.focusTracker.onDidFocus(() => this.updateFocusedItem()));
|
||||
this.actionsList = document.createElement('ul');
|
||||
this.actionsList.className = 'actions-container';
|
||||
this.actionsList.setAttribute('role', this.options.ariaRole || 'toolbar');
|
||||
if (this.options.ariaLabel) {
|
||||
this.actionsList.setAttribute('aria-label', this.options.ariaLabel);
|
||||
}
|
||||
this.domNode.appendChild(this.actionsList);
|
||||
container.appendChild(this.domNode);
|
||||
}
|
||||
refreshRole() {
|
||||
if (this.length() >= 2) {
|
||||
this.actionsList.setAttribute('role', this.options.ariaRole || 'toolbar');
|
||||
}
|
||||
else {
|
||||
this.actionsList.setAttribute('role', 'presentation');
|
||||
}
|
||||
}
|
||||
// Some action bars should not be focusable at times
|
||||
// When an action bar is not focusable make sure to make all the elements inside it not focusable
|
||||
// When an action bar is focusable again, make sure the first item can be focused
|
||||
setFocusable(focusable) {
|
||||
this.focusable = focusable;
|
||||
if (this.focusable) {
|
||||
const firstEnabled = this.viewItems.find(vi => vi instanceof BaseActionViewItem && vi.isEnabled());
|
||||
if (firstEnabled instanceof BaseActionViewItem) {
|
||||
firstEnabled.setFocusable(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.viewItems.forEach(vi => {
|
||||
if (vi instanceof BaseActionViewItem) {
|
||||
vi.setFocusable(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
isTriggerKeyEvent(event) {
|
||||
let ret = false;
|
||||
this._triggerKeys.keys.forEach(keyCode => {
|
||||
ret = ret || event.equals(keyCode);
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
updateFocusedItem() {
|
||||
for (let i = 0; i < this.actionsList.children.length; i++) {
|
||||
const elem = this.actionsList.children[i];
|
||||
if (DOM.isAncestor(DOM.getActiveElement(), elem)) {
|
||||
this.focusedItem = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
get context() {
|
||||
return this._context;
|
||||
}
|
||||
set context(context) {
|
||||
this._context = context;
|
||||
this.viewItems.forEach(i => i.setActionContext(context));
|
||||
}
|
||||
get actionRunner() {
|
||||
return this._actionRunner;
|
||||
}
|
||||
set actionRunner(actionRunner) {
|
||||
if (actionRunner) {
|
||||
this._actionRunner = actionRunner;
|
||||
this.viewItems.forEach(item => item.actionRunner = actionRunner);
|
||||
}
|
||||
}
|
||||
getContainer() {
|
||||
return this.domNode;
|
||||
}
|
||||
push(arg, options = {}) {
|
||||
const actions = Array.isArray(arg) ? arg : [arg];
|
||||
let index = types.isNumber(options.index) ? options.index : null;
|
||||
actions.forEach((action) => {
|
||||
const actionViewItemElement = document.createElement('li');
|
||||
actionViewItemElement.className = 'action-item';
|
||||
actionViewItemElement.setAttribute('role', 'presentation');
|
||||
let item;
|
||||
if (this.options.actionViewItemProvider) {
|
||||
item = this.options.actionViewItemProvider(action);
|
||||
}
|
||||
if (!item) {
|
||||
item = new ActionViewItem(this.context, action, Object.assign({ hoverDelegate: this.options.hoverDelegate }, options));
|
||||
}
|
||||
// Prevent native context menu on actions
|
||||
if (!this.options.allowContextMenu) {
|
||||
this.viewItemDisposables.set(item, DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e) => {
|
||||
DOM.EventHelper.stop(e, true);
|
||||
}));
|
||||
}
|
||||
item.actionRunner = this._actionRunner;
|
||||
item.setActionContext(this.context);
|
||||
item.render(actionViewItemElement);
|
||||
if (this.focusable && item instanceof BaseActionViewItem && this.viewItems.length === 0) {
|
||||
// We need to allow for the first enabled item to be focused on using tab navigation #106441
|
||||
item.setFocusable(true);
|
||||
}
|
||||
if (index === null || index < 0 || index >= this.actionsList.children.length) {
|
||||
this.actionsList.appendChild(actionViewItemElement);
|
||||
this.viewItems.push(item);
|
||||
this._actionIds.push(action.id);
|
||||
}
|
||||
else {
|
||||
this.actionsList.insertBefore(actionViewItemElement, this.actionsList.children[index]);
|
||||
this.viewItems.splice(index, 0, item);
|
||||
this._actionIds.splice(index, 0, action.id);
|
||||
index++;
|
||||
}
|
||||
});
|
||||
if (typeof this.focusedItem === 'number') {
|
||||
// After a clear actions might be re-added to simply toggle some actions. We should preserve focus #97128
|
||||
this.focus(this.focusedItem);
|
||||
}
|
||||
this.refreshRole();
|
||||
}
|
||||
clear() {
|
||||
dispose(this.viewItems);
|
||||
this.viewItemDisposables.forEach(d => d.dispose());
|
||||
this.viewItemDisposables.clear();
|
||||
this.viewItems = [];
|
||||
this._actionIds = [];
|
||||
DOM.clearNode(this.actionsList);
|
||||
this.refreshRole();
|
||||
}
|
||||
length() {
|
||||
return this.viewItems.length;
|
||||
}
|
||||
focus(arg) {
|
||||
let selectFirst = false;
|
||||
let index = undefined;
|
||||
if (arg === undefined) {
|
||||
selectFirst = true;
|
||||
}
|
||||
else if (typeof arg === 'number') {
|
||||
index = arg;
|
||||
}
|
||||
else if (typeof arg === 'boolean') {
|
||||
selectFirst = arg;
|
||||
}
|
||||
if (selectFirst && typeof this.focusedItem === 'undefined') {
|
||||
const firstEnabled = this.viewItems.findIndex(item => item.isEnabled());
|
||||
// Focus the first enabled item
|
||||
this.focusedItem = firstEnabled === -1 ? undefined : firstEnabled;
|
||||
this.updateFocus(undefined, undefined, true);
|
||||
}
|
||||
else {
|
||||
if (index !== undefined) {
|
||||
this.focusedItem = index;
|
||||
}
|
||||
this.updateFocus(undefined, undefined, true);
|
||||
}
|
||||
}
|
||||
focusFirst() {
|
||||
this.focusedItem = this.length() - 1;
|
||||
return this.focusNext(true);
|
||||
}
|
||||
focusLast() {
|
||||
this.focusedItem = 0;
|
||||
return this.focusPrevious(true);
|
||||
}
|
||||
focusNext(forceLoop) {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
this.focusedItem = this.viewItems.length - 1;
|
||||
}
|
||||
else if (this.viewItems.length <= 1) {
|
||||
return false;
|
||||
}
|
||||
const startIndex = this.focusedItem;
|
||||
let item;
|
||||
do {
|
||||
if (!forceLoop && this.options.preventLoopNavigation && this.focusedItem + 1 >= this.viewItems.length) {
|
||||
this.focusedItem = startIndex;
|
||||
return false;
|
||||
}
|
||||
this.focusedItem = (this.focusedItem + 1) % this.viewItems.length;
|
||||
item = this.viewItems[this.focusedItem];
|
||||
} while (this.focusedItem !== startIndex && ((this.options.focusOnlyEnabledItems && !item.isEnabled()) || item.action.id === Separator.ID));
|
||||
this.updateFocus();
|
||||
return true;
|
||||
}
|
||||
focusPrevious(forceLoop) {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
this.focusedItem = 0;
|
||||
}
|
||||
else if (this.viewItems.length <= 1) {
|
||||
return false;
|
||||
}
|
||||
const startIndex = this.focusedItem;
|
||||
let item;
|
||||
do {
|
||||
this.focusedItem = this.focusedItem - 1;
|
||||
if (this.focusedItem < 0) {
|
||||
if (!forceLoop && this.options.preventLoopNavigation) {
|
||||
this.focusedItem = startIndex;
|
||||
return false;
|
||||
}
|
||||
this.focusedItem = this.viewItems.length - 1;
|
||||
}
|
||||
item = this.viewItems[this.focusedItem];
|
||||
} while (this.focusedItem !== startIndex && ((this.options.focusOnlyEnabledItems && !item.isEnabled()) || item.action.id === Separator.ID));
|
||||
this.updateFocus(true);
|
||||
return true;
|
||||
}
|
||||
updateFocus(fromRight, preventScroll, forceFocus = false) {
|
||||
var _a;
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
this.actionsList.focus({ preventScroll });
|
||||
}
|
||||
if (this.previouslyFocusedItem !== undefined && this.previouslyFocusedItem !== this.focusedItem) {
|
||||
(_a = this.viewItems[this.previouslyFocusedItem]) === null || _a === void 0 ? void 0 : _a.blur();
|
||||
}
|
||||
const actionViewItem = this.focusedItem !== undefined && this.viewItems[this.focusedItem];
|
||||
if (actionViewItem) {
|
||||
let focusItem = true;
|
||||
if (!types.isFunction(actionViewItem.focus)) {
|
||||
focusItem = false;
|
||||
}
|
||||
if (this.options.focusOnlyEnabledItems && types.isFunction(actionViewItem.isEnabled) && !actionViewItem.isEnabled()) {
|
||||
focusItem = false;
|
||||
}
|
||||
if (actionViewItem.action.id === Separator.ID) {
|
||||
focusItem = false;
|
||||
}
|
||||
if (!focusItem) {
|
||||
this.actionsList.focus({ preventScroll });
|
||||
this.previouslyFocusedItem = undefined;
|
||||
}
|
||||
else if (forceFocus || this.previouslyFocusedItem !== this.focusedItem) {
|
||||
actionViewItem.focus(fromRight);
|
||||
this.previouslyFocusedItem = this.focusedItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
doTrigger(event) {
|
||||
if (typeof this.focusedItem === 'undefined') {
|
||||
return; //nothing to focus
|
||||
}
|
||||
// trigger action
|
||||
const actionViewItem = this.viewItems[this.focusedItem];
|
||||
if (actionViewItem instanceof BaseActionViewItem) {
|
||||
const context = (actionViewItem._context === null || actionViewItem._context === undefined) ? event : actionViewItem._context;
|
||||
this.run(actionViewItem._action, context);
|
||||
}
|
||||
}
|
||||
run(action, context) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield this._actionRunner.run(action, context);
|
||||
});
|
||||
}
|
||||
dispose() {
|
||||
dispose(this.viewItems);
|
||||
this.viewItems = [];
|
||||
this._actionIds = [];
|
||||
this.getContainer().remove();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user