Adding inline actions to OE (#23101)

This commit is contained in:
Aasim Khan
2023-05-13 09:24:49 -07:00
committed by GitHub
parent 31a88cc9eb
commit 2beba9ac08
18 changed files with 279 additions and 66 deletions

View File

@@ -46,14 +46,6 @@
color: #ffffff;
}
.server-explorer-viewlet .monaco-action-bar .action-label {
margin-right: 0.3em;
margin-left: 0.3em;
line-height: 15px;
width: 10px !important;
height: 10px !important;
}
/* Add space beneath the button */
.new-connection .monaco-text-button {
margin-bottom: 2px;
@@ -276,3 +268,21 @@
background-repeat: no-repeat;
background-size: 16px 16px;
}
.server-explorer-viewlet .monaco-list .monaco-list-row .actions {
display: none;
padding-right: 10px;
}
.server-explorer-viewlet .monaco-list .monaco-list-row .actions .action-label {
background-size: 14px 14px;
background-repeat: no-repeat;
background-position: center;
}
.server-explorer-viewlet .monaco-list .monaco-list-row:hover .actions,
.server-explorer-viewlet .monaco-list .monaco-list-row.selected .actions,
.server-explorer-viewlet .monaco-list .monaco-list-row.focused .actions {
display: block;
}

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path d="M0,1H16V2.7l-6,6V15H6V8.7l-6-6ZM15,2.3V2H1v.3l6,6V14H9V8.3Zm.3,7.7.7.7-1.8,1.8L16,14.3l-.7.7-1.8-1.8L11.7,15l-.7-.7,1.8-1.8L11,10.7l.7-.7,1.8,1.8Z" />
</svg>

After

Width:  |  Height:  |  Size: 253 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
<path fill="#fff" d="M0,1H16V2.7l-6,6V15H6V8.7l-6-6ZM15,2.3V2H1v.3l6,6V14H9V8.3Zm.3,7.7.7.7-1.8,1.8L16,14.3l-.7.7-1.8-1.8L11.7,15l-.7-.7,1.8-1.8L11,10.7l.7-.7,1.8,1.8Z" />
</svg>

After

Width:  |  Height:  |  Size: 265 B

View File

@@ -10,7 +10,8 @@
}
.monaco-workbench.vs-dark .actions-container .action-label.add-server-action,
.monaco-workbench.hc-black .actions-container .action-label.add-server-action {
.monaco-workbench.hc-black .actions-container .action-label.add-server-action,
.monaco-list:focus .monaco-list-rows .actions-container .action-label.add-server-action {
background-image: url('add_server_inverse.svg') !important;
}
@@ -44,6 +45,17 @@
background-image: url('server_page_inverse.svg') !important;
}
.monaco-workbench .actions-container .action-label.remove-filter-action,
.monaco-workbench.hc-light .actions-container .action-label.remove-filter-action {
background-image: url('remove_filter.svg') !important;
}
.monaco-workbench.vs-dark .actions-container .action-label.remove-filter-action,
.monaco-workbench.hc-black .actions-container .action-label.remove-filter-action,
.monaco-list:focus .monaco-list-rows .actions-container .action-label.remove-filter-action {
background-image: url('remove_filter_inverse.svg') !important;
}
/* styles for action labels - without these, the buttons for the Servers view will not be sized correctly */
.monaco-pane-view .pane > .pane-header > .actions .action-label.icon,
.monaco-pane-view .pane > .pane-header > .actions .action-label.codicon {

View File

@@ -930,7 +930,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
}
private getActionContext(element: ServerTreeElement): any {
public getActionContext(element: ServerTreeElement): any {
let actionContext: any;
if (element instanceof TreeNode) {
let context = new ObjectExplorerActionsContext();

View File

@@ -17,6 +17,7 @@ import { ItemContextKey } from 'sql/workbench/contrib/dashboard/browser/widgets/
import { EditDataAction } from 'sql/workbench/browser/scriptingActions';
import { DatabaseEngineEdition } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ServerInfoContextKey } from 'sql/workbench/services/connection/common/serverInfoContextKey';
import { Codicon } from 'vs/base/common/codicons';
//#region -- Data Explorer
// Script as Create
@@ -287,6 +288,27 @@ MenuRegistry.appendMenuItem(MenuId.ObjectExplorerItemContext, {
TreeNodeContextKey.NodeType.isEqualTo(NodeType.TableValuedFunction))
});
MenuRegistry.appendMenuItem(MenuId.ObjectExplorerItemContext, {
group: 'inline',
order: 7,
command: {
id: commands.OE_REFRESH_COMMAND_ID,
title: localize('refreshNode', "Refresh"),
icon: Codicon.refresh
},
when: ContextKeyExpr.or(
TreeNodeContextKey.NodeType.isEqualTo(NodeType.Table),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.View),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.Schema),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.User),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.UserDefinedTableType),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.StoredProcedure),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.AggregateFunction),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.PartitionFunction),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.ScalarValuedFunction),
TreeNodeContextKey.NodeType.isEqualTo(NodeType.TableValuedFunction))
});
//#endregion
//#region -- explorer widget

View File

@@ -46,6 +46,7 @@ import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserve
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { onUnexpectedError } from 'vs/base/common/errors';
import { FieldSet } from 'sql/base/browser/ui/fieldset/fieldset';
import { KeyCode } from 'vs/base/common/keyCodes';
export interface OnShowUIResponse {
selectedProviderDisplayName: string;
@@ -357,34 +358,56 @@ export class ConnectionDialogWidget extends Modal {
};
const actionProvider = this.instantiationService.createInstance(RecentConnectionActionsProvider);
const controller = new RecentConnectionTreeController(leftClick, actionProvider, this.connectionManagementService, this.contextMenuService);
actionProvider.onRecentConnectionRemoved(() => {
const recentConnections: ConnectionProfile[] = this.connectionManagementService.getRecentConnections();
this.open(recentConnections.length > 0).catch(err => this.logService.error(`Unexpected error opening connection widget after a recent connection was removed from action provider: ${err}`));
});
controller.onRecentConnectionRemoved(() => {
const recentConnections: ConnectionProfile[] = this.connectionManagementService.getRecentConnections();
this.open(recentConnections.length > 0).catch(err => this.logService.error(`Unexpected error opening connection widget after a recent connection was removed from controller : ${err}`));
});
this._register(actionProvider.onRecentConnectionRemoved(async () => {
await this.refreshTree();
}));
this._register(controller.onRecentConnectionRemoved(async () => {
await this.refreshTree();
}));
this._register(this.connectionManagementService.onRecentConnectionProfileDeleted(async (e) => {
await this.refreshTree();
}));
this._recentConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer, this.instantiationService, this._configurationService, localize('connectionDialog.recentConnections', "Recent Connections"), controller);
if (this._recentConnectionTree instanceof AsyncServerTree) {
this._recentConnectionTree.onMouseClick(e => {
this._register(this._recentConnectionTree.onMouseClick(e => {
if (e.element instanceof ConnectionProfile) {
this._connectionSource = 'recent';
this.onConnectionClick(e.element, false).catch(onUnexpectedError);
}
});
this._recentConnectionTree.onMouseDblClick(e => {
}));
this._register(this._recentConnectionTree.onMouseDblClick(e => {
if (e.element instanceof ConnectionProfile) {
this._connectionSource = 'recent';
this.onConnectionClick(e.element, true).catch(onUnexpectedError);
}
});
}));
this._register(this._recentConnectionTree.onKeyDown(e => {
const keyboardEvent = new StandardKeyboardEvent(e);
if (keyboardEvent.keyCode === KeyCode.Delete) {
const element = this._recentConnectionTree.getSelection()[0];
if (element instanceof ConnectionProfile) {
this.connectionManagementService.clearRecentConnection(element);
}
}
}));
}
// Theme styler
this._register(styler.attachListStyler(this._recentConnectionTree, this._themeService));
}
private async refreshTree() {
try {
const recentConnections: ConnectionProfile[] = this.connectionManagementService.getRecentConnections();
await this.open(recentConnections.length > 0);
} catch (err) {
this.logService.error(`Unexpected error opening connection widget after a recent connection was removed from controller : ${err}`);
}
}
private createRecentConnections() {
this.createRecentConnectionList();
const noRecentConnectionContainer = DOM.append(this._noRecentConnection, DOM.$('.connection-recent-content'));

View File

@@ -88,6 +88,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
private _onConnectionProfileGroupEdited = new Emitter<ConnectionProfileGroup>();
private _onConnectionProfileGroupMoved = new Emitter<ConnectionElementMovedParams>();
private _onRecentConnectionProfileDeleted = new Emitter<ConnectionProfile>();
private _mementoContext: Memento;
private _mementoObj: MementoObject;
private _connectionStore: ConnectionStore;
@@ -246,6 +248,10 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return this._onConnectionProfileGroupMoved.event;
}
public get onRecentConnectionProfileDeleted(): Event<ConnectionProfile> {
return this._onRecentConnectionProfileDeleted.event;
}
public get providerNameToDisplayNameMap(): { readonly [providerDisplayName: string]: string } {
return this._providerNameToDisplayNameMap;
}
@@ -829,6 +835,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
public clearRecentConnection(connectionProfile: interfaces.IConnectionProfile): void {
this._connectionStore.removeRecentConnection(connectionProfile);
this._onRecentConnectionProfileDeleted.fire(<ConnectionProfile>connectionProfile);
}
public getActiveConnections(providers?: string[]): ConnectionProfile[] {

View File

@@ -53,6 +53,7 @@ import { TestConfigurationService } from 'sql/platform/connection/test/common/te
import { ConnectionTreeService, IConnectionTreeService } from 'sql/workbench/services/connection/common/connectionTreeService';
import { ConnectionBrowserView } from 'sql/workbench/services/connection/browser/connectionBrowseTab';
import { ConnectionProviderProperties, ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { Emitter } from 'vs/base/common/event';
suite('ConnectionDialogService tests', () => {
const testTreeViewId = 'testTreeView';
@@ -90,7 +91,7 @@ suite('ConnectionDialogService tests', () => {
testInstantiationService.stub(IViewDescriptorService, viewDescriptorService);
let errorMessageService = getMockErrorMessageService();
let capabilitiesService = new TestCapabilitiesService();
mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict,
mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose,
undefined, // connection dialog service
testInstantiationService, // instantiation service
undefined, // editor service
@@ -129,6 +130,7 @@ suite('ConnectionDialogService tests', () => {
}
};
});
mockConnectionManagementService.setup(x => x.onRecentConnectionProfileDeleted).returns(() => new Emitter<ConnectionProfile>().event);
testConnectionDialog = new TestConnectionDialogWidget(providerDisplayNames, providerNameToDisplayMap['MSSQL'], providerNameToDisplayMap, testInstantiationService, mockConnectionManagementService.object, undefined, undefined, viewDescriptorService, new TestThemeService(), new TestLayoutService(), new NullAdsTelemetryService(), new MockContextKeyService(), undefined, new NullLogService(), new TestTextResourcePropertiesService(new TestConfigurationService), new TestConfigurationService(), new TestCapabilitiesService());
testConnectionDialog.render();
testConnectionDialog['renderBody'](DOM.createStyleSheet());
@@ -184,6 +186,7 @@ suite('ConnectionDialogService tests', () => {
return Promise.resolve(connectionProfile);
});
mockConnectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => true);
mockWidget = TypeMoq.Mock.ofType(ConnectionWidget, TypeMoq.MockBehavior.Strict, [], undefined, 'MSSQL', undefined, undefined, mockConnectionManagementService.object);
mockWidget.setup(x => x.focusOnOpen());
mockWidget.setup(x => x.handleOnConnecting());

View File

@@ -29,6 +29,9 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { TestTreeView } from 'sql/workbench/services/connection/test/browser/testTreeView';
import { ConnectionTreeService, IConnectionTreeService } from 'sql/workbench/services/connection/common/connectionTreeService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Emitter } from 'vs/base/common/event';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
suite('ConnectionDialogWidget tests', () => {
const testTreeViewId = 'testTreeView';
const ViewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
@@ -66,6 +69,7 @@ suite('ConnectionDialogWidget tests', () => {
mockConnectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => true);
mockConnectionManagementService.setup(x => x.getConnectionIconId(TypeMoq.It.isAnyString())).returns(() => '');
mockConnectionManagementService.setup(x => x.getProviderProperties(TypeMoq.It.isAnyString())).returns(() => undefined);
mockConnectionManagementService.setup(x => x.onRecentConnectionProfileDeleted).returns(() => new Emitter<ConnectionProfile>().event);
cmInstantiationService.stub(IConnectionManagementService, mockConnectionManagementService.object);
let providerDisplayNames = ['Mock SQL Server'];
let providerNameToDisplayMap = { 'MSSQL': 'Mock SQL Server' };

View File

@@ -24,6 +24,7 @@ import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/comm
import { instanceOfSqlThemeIcon } from 'sql/workbench/services/objectExplorer/common/nodeType';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
import { ResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar } from 'sql/base/browser/ui/taskbar/actionbar';
const DefaultConnectionIconClass = 'server-page';
export interface ConnectionProfileGroupDisplayOptions {
@@ -35,11 +36,13 @@ class ConnectionProfileGroupTemplate extends Disposable {
private _icon: HTMLElement;
private _labelContainer: HTMLElement;
private _label: ResourceLabel;
private _actionBar: ActionBar;
constructor(
container: HTMLElement,
private _option: ConnectionProfileGroupDisplayOptions,
@IInstantiationService private readonly _instantiationService: IInstantiationService
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService
) {
super();
container.parentElement!.classList.add('async-server-group');
@@ -48,6 +51,9 @@ class ConnectionProfileGroupTemplate extends Disposable {
this._icon = dom.append(this._root, dom.$('div.icon'));
this._labelContainer = dom.append(this._root, dom.$('span.name'));
this._label = this._instantiationService.createInstance(ResourceLabel, this._labelContainer, { supportHighlights: true });
const actionsContainer = dom.append(this._label.element.element, dom.$('.actions'));
this._actionBar = new ActionBar(actionsContainer, {
});
}
set(element: ConnectionProfileGroup, filterData: FuzzyScore) {
@@ -63,6 +69,13 @@ class ConnectionProfileGroupTemplate extends Disposable {
this._label.element.setLabel(element.name, '', {
matches: createMatches(filterData)
});
const actionProvider = this._objectExplorerService.getServerTreeView().treeActionProvider;
const tree = this._objectExplorerService.getServerTreeView().tree;
const actions = actionProvider.getActions(tree, element, true);
this._actionBar.context = this._objectExplorerService.getServerTreeView().getActionContext(element);
this._actionBar.clear();
this._actionBar.pushAction(actions, { icon: true, label: false });
}
}
@@ -91,6 +104,8 @@ class ConnectionProfileTemplate extends Disposable {
private _connectionStatusBadge: HTMLElement;
private _labelContainer: HTMLElement;
private _label: ResourceLabel;
private _actionBar: ActionBar;
/**
* _isCompact is used to render connections tiles with and without the action buttons.
* When set to true, like in the connection dialog recent connections tree, the connection
@@ -110,6 +125,9 @@ class ConnectionProfileTemplate extends Disposable {
this._connectionStatusBadge = dom.append(this._icon, dom.$('div.connection-status-badge'));
this._labelContainer = dom.append(this._root, dom.$('div.label'));
this._label = this._instantiationService.createInstance(ResourceLabel, this._labelContainer, { supportHighlights: true });
const actionsContainer = dom.append(this._label.element.element, dom.$('.actions'));
this._actionBar = new ActionBar(actionsContainer, {
});
}
set(element: ConnectionProfile, filterData: FuzzyScore) {
@@ -132,6 +150,19 @@ class ConnectionProfileTemplate extends Disposable {
matches: createMatches(filterData)
});
this._root.title = labelText;
const actionProvider = this._objectExplorerService.getServerTreeView().treeActionProvider;
if (!this._isCompact) {
const tree = this._objectExplorerService.getServerTreeView().tree;
const actions = actionProvider.getActions(tree, element, true);
this._actionBar.context = this._objectExplorerService.getServerTreeView().getActionContext(element);
this._actionBar.clear();
this._actionBar.pushAction(actions, { icon: true, label: false });
} else {
const actions = actionProvider.getRecentConnectionActions(element);
this._actionBar.context = undefined;
this._actionBar.clear();
this._actionBar.pushAction(actions, { icon: true, label: false });
}
}
}
@@ -159,16 +190,21 @@ class TreeNodeTemplate extends Disposable {
private _icon: HTMLElement;
private _labelContainer: HTMLElement;
private _label: ResourceLabel;
private _actionBar: ActionBar;
constructor(
container: HTMLElement,
@IInstantiationService private readonly _instantiationService: IInstantiationService
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService
) {
super();
this._root = dom.append(container, dom.$('.object-element-container'));
this._icon = dom.append(this._root, dom.$('div.object-icon'));
this._labelContainer = dom.append(this._root, dom.$('div.label'));
this._label = this._instantiationService.createInstance(ResourceLabel, this._labelContainer, { supportHighlights: true });
const actionsContainer = dom.append(this._label.element.element, dom.$('.actions'));
this._actionBar = new ActionBar(actionsContainer, {
});
}
set(element: TreeNode, filterData: FuzzyScore) {
@@ -211,6 +247,12 @@ class TreeNodeTemplate extends Disposable {
matches: createMatches(filterData)
});
this._root.title = labelText;
const tree = this._objectExplorerService.getServerTreeView().tree;
const actionProvider = this._objectExplorerService.getServerTreeView().treeActionProvider;
const actions = actionProvider.getActions(tree, element, true);
this._actionBar.context = this._objectExplorerService.getServerTreeView().getActionContext(element);
this._actionBar.clear();
this._actionBar.pushAction(actions, { icon: true, label: false });
}
}

View File

@@ -21,6 +21,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
import { SqlIconId } from 'sql/base/common/codicons';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { Codicon } from 'vs/base/common/codicons';
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
@@ -44,7 +45,7 @@ export class RefreshAction extends Action {
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@ILogService private _logService: ILogService
) {
super(id, label);
super(id, label, Codicon.refresh.classNames);
}
public override async run(): Promise<void> {
let treeNode: TreeNode | undefined = undefined;
@@ -103,8 +104,7 @@ export class EditConnectionAction extends Action {
private _connectionProfile: ConnectionProfile,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) {
super(id, label);
this.class = 'edit-server-action';
super(id, label, Codicon.edit.classNames);
}
public override async run(): Promise<void> {
@@ -124,7 +124,7 @@ export class DisconnectConnectionAction extends Action {
private _connectionProfile: ConnectionProfile,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) {
super(id, label);
super(id, label, Codicon.debugDisconnect.classNames);
}
override async run(actionContext: ObjectExplorerActionsContext): Promise<any> {
@@ -223,8 +223,7 @@ export class EditServerGroupAction extends Action {
private _group: ConnectionProfileGroup,
@IServerGroupController private readonly serverGroupController: IServerGroupController
) {
super(id, label);
this.class = 'edit-server-group-action';
super(id, label, Codicon.edit.classNames);
}
public override run(): Promise<void> {
@@ -277,8 +276,7 @@ export class DeleteConnectionAction extends Action {
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IDialogService private _dialogService: IDialogService
) {
super(id, label);
this.class = 'delete-connection-action';
super(id, label, Codicon.trash.classNames);
if (element instanceof ConnectionProfileGroup && element.id === UNSAVED_GROUP_ID) {
this.enabled = false;
}
@@ -322,14 +320,19 @@ export class FilterChildrenAction extends Action {
label: string,
private _node: TreeNode,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService) {
super(id, label);
super(id, label, getFilterActionIconClass(_node));
}
public override async run(): Promise<void> {
await this._objectExplorerService.getServerTreeView().filterElementChildren(this._node);
this.class = getFilterActionIconClass(this._node);
}
}
function getFilterActionIconClass(node: TreeNode): string {
return node.filters.length > 0 ? Codicon.filterFilled.classNames : Codicon.filter.classNames;
}
export class RemoveFilterAction extends Action {
public static ID = 'objectExplorer.removeFilter';
public static LABEL = localize('objectExplorer.removeFilter', "Remove Filter");
@@ -343,7 +346,7 @@ export class RemoveFilterAction extends Action {
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IAdsTelemetryService private _telemetryService: IAdsTelemetryService
) {
super(id, label);
super(id, label, SqlIconId.removeFilter);
}
public override async run(): Promise<void> {
@@ -373,3 +376,23 @@ export class RemoveFilterAction extends Action {
}).send();
}
}
export class DeleteRecentConnectionsAction extends Action {
public static ID = 'registeredServers.clearRecentConnections';
public static LABEL = localize('registeredServers.clearRecentConnections', "Delete");
constructor(
id: string,
label: string,
private _connectionProfile: ConnectionProfile,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) {
super(id, label, Codicon.trash.classNames);
}
public override async run(): Promise<void> {
if (this._connectionProfile) {
this._connectionManagementService.clearRecentConnection(this._connectionProfile);
}
}
}

View File

@@ -56,6 +56,7 @@ export interface IServerTreeView {
layout(size: number): void;
showFilteredTree(view: ServerTreeViewView): void;
filterElementChildren(node: TreeNode): Promise<void>;
getActionContext(element: ServerTreeElement): any;
view: ServerTreeViewView;
}

View File

@@ -10,7 +10,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import {
DisconnectConnectionAction, EditConnectionAction,
DeleteConnectionAction, RefreshAction, EditServerGroupAction, AddServerAction, FilterChildrenAction, RemoveFilterAction
DeleteConnectionAction, RefreshAction, EditServerGroupAction, AddServerAction, FilterChildrenAction, RemoveFilterAction, DeleteRecentConnectionsAction
} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType';
@@ -52,9 +52,9 @@ export class ServerTreeActionProvider {
/**
* Return actions given an element in the tree
*/
public getActions(tree: AsyncServerTree | ITree, element: ServerTreeElement): IAction[] {
public getActions(tree: AsyncServerTree | ITree, element: ServerTreeElement, inlineOnly: boolean = false): IAction[] {
if (element instanceof ConnectionProfile) {
return this.getConnectionActions(tree, element);
return this.getConnectionActions(tree, element, inlineOnly);
}
if (element instanceof ConnectionProfileGroup) {
return this.getConnectionProfileGroupActions(element);
@@ -66,7 +66,7 @@ export class ServerTreeActionProvider {
tree: tree,
profile,
treeNode: element
});
}, inlineOnly);
}
}
return [];
@@ -95,10 +95,18 @@ export class ServerTreeActionProvider {
return undefined;
}
public getRecentConnectionActions(element: ConnectionProfile): IAction[] {
return [
this._instantiationService.createInstance(EditConnectionAction, EditConnectionAction.ID, EditConnectionAction.LABEL, element),
this._instantiationService.createInstance(DeleteRecentConnectionsAction, DeleteRecentConnectionsAction.ID, DeleteRecentConnectionsAction.LABEL, element)
]
}
/**
* Return actions for connection elements
*/
private getConnectionActions(tree: AsyncServerTree | ITree, profile: ConnectionProfile): IAction[] {
private getConnectionActions(tree: AsyncServerTree | ITree, profile: ConnectionProfile, inlineOnly: boolean = false): IAction[] {
let node = new TreeNode(NodeType.Server, NodeType.Server, '', false, '', '', '', '', undefined, undefined, undefined, undefined);
// Only update password and not access tokens to avoid login prompts when opening context menu.
this._connectionManagementService.addSavedPassword(profile, true);
@@ -107,10 +115,11 @@ export class ServerTreeActionProvider {
tree: tree,
profile: profile,
treeNode: node
}, (context) => this.getBuiltinConnectionActions(context));
}, (context) => this.getBuiltinConnectionActions(context),
inlineOnly);
}
private getAllActions(context: ObjectExplorerContext, getDefaultActions: (context: ObjectExplorerContext) => IAction[]) {
private getAllActions(context: ObjectExplorerContext, getDefaultActions: (context: ObjectExplorerContext) => IAction[], inlineOnly: boolean = false) {
// Create metadata needed to get a useful set of actions
let scopedContextService = this.getContextKeyService(context);
let menu = this.menuService.createMenu(MenuId.ObjectExplorerItemContext, scopedContextService);
@@ -118,8 +127,11 @@ export class ServerTreeActionProvider {
// Fill in all actions
const builtIn = getDefaultActions(context);
const actions: IAction[] = [];
const options = { arg: undefined, shouldForwardArgs: true };
const groups = menu.getActions(options);
const options = {
arg: undefined, shouldForwardArgs: true
};
let groups = menu.getActions(options);
let insertIndex: number | undefined = 0;
const queryIndex = groups.findIndex(v => {
if (v[0] === '0_query') {
@@ -132,24 +144,36 @@ export class ServerTreeActionProvider {
}
});
insertIndex = queryIndex > -1 ? insertIndex + groups[queryIndex][1].length : undefined;
fillInActions(groups, actions, false);
if (insertIndex) {
if (!(actions[insertIndex] instanceof Separator) && builtIn.length > 0) {
builtIn.unshift(new Separator());
}
actions?.splice(insertIndex, 0, ...builtIn);
} else {
if (actions.length > 0 && builtIn.length > 0) {
builtIn.push(new Separator());
}
if (inlineOnly) {
groups = groups.filter(g => g[0].includes('inline'));
fillInActions(groups, actions, false);
actions.unshift(...builtIn);
// Moving refresh action to the end of the list
const refreshIndex = actions.findIndex(f => {
return f instanceof RefreshAction;
});
if (refreshIndex > -1) {
actions.push(actions.splice(refreshIndex, 1)[0]);
}
} else {
fillInActions(groups, actions, false);
if (insertIndex) {
if (!(actions[insertIndex] instanceof Separator) && builtIn.length > 0 && !inlineOnly) {
builtIn.unshift(new Separator());
}
actions?.splice(insertIndex, 0, ...builtIn);
} else {
if (actions.length > 0 && builtIn.length > 0) {
builtIn.push(new Separator());
}
actions.unshift(...builtIn);
}
}
// Cleanup
menu.dispose();
return actions;
}
private getBuiltinConnectionActions(context: ObjectExplorerContext): IAction[] {
@@ -209,8 +233,8 @@ export class ServerTreeActionProvider {
/**
* Return actions for OE elements
*/
private getObjectExplorerNodeActions(context: ObjectExplorerContext): IAction[] {
return this.getAllActions(context, (context) => this.getBuiltInNodeActions(context));
private getObjectExplorerNodeActions(context: ObjectExplorerContext, inlineOnly: boolean = false): IAction[] {
return this.getAllActions(context, (context) => this.getBuiltInNodeActions(context), inlineOnly);
}
private getBuiltInNodeActions(context: ObjectExplorerContext): IAction[] {
@@ -226,16 +250,15 @@ export class ServerTreeActionProvider {
}
// Contribute refresh action for scriptable objects via contribution
if (!this.isScriptableObject(context)) {
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, context.treeNode || context.profile));
// Adding filter action if the node has filter properties
if (treeNode?.filterProperties?.length > 0 && this._configurationService.getValue<boolean>(CONFIG_WORKBENCH_ENABLEPREVIEWFEATURES)) {
actions.push(this._instantiationService.createInstance(FilterChildrenAction, FilterChildrenAction.ID, FilterChildrenAction.LABEL, context.treeNode));
}
// Adding remove filter action if the node has filters applied to it.
// Adding remove filter action if the node has filters applied to it and the action is not inline only.
if (treeNode?.filters?.length > 0) {
actions.push(this._instantiationService.createInstance(RemoveFilterAction, RemoveFilterAction.ID, RemoveFilterAction.LABEL, context.treeNode, context.tree, undefined));
}
actions.push(this._instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL, context.tree, context.treeNode || context.profile));
}
return actions;
}