mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-21 17:22:55 -05:00
Execution Plan Filtering and UI improvements (#20631)
* Fixing execution plan stuff * Adding filter to top operations * Flipped custom zoom icon * changing keys * Moving constants to a file * Search properties * Making logic concise
This commit is contained in:
@@ -70,10 +70,13 @@ export class AzdataGraphView {
|
||||
this.onElementSelected = this._onElementSelectedEmitter.event;
|
||||
this._diagram.graph.getSelectionModel().addListener('change', (sender, evt) => {
|
||||
if (evt.properties?.removed) {
|
||||
if (this._cellInFocus?.id === evt.properties.removed[0].id) {
|
||||
if (this._cellInFocus?.id === evt.properties.removed[0]?.id) {
|
||||
return;
|
||||
}
|
||||
const newSelection = evt.properties.removed[0];
|
||||
if (!newSelection) {
|
||||
return;
|
||||
}
|
||||
this._onElementSelectedEmitter.fire(this.getElementById(newSelection.id));
|
||||
this.centerElement(this.getElementById(newSelection.id));
|
||||
this._cellInFocus = evt.properties.removed[0];
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
let imageBasePath = require.toUrl('./images/icons/');
|
||||
export let executionPlanNodeIconPaths =
|
||||
{
|
||||
@@ -366,3 +368,8 @@ export const polygonFillColor: string[] = [
|
||||
`rgba(70, 104, 197, 0.1)`, // "f2 blue purple"
|
||||
`rgba(226, 229, 132, 0.1)`, // "j2 khaki"
|
||||
];
|
||||
|
||||
//constant strings
|
||||
export const propertiesSearchDescription = localize('ep.propertiesSearchDescription', 'Search properties table');
|
||||
export const topOperationsSearchDescription = localize('ep.topOperationsSearchDescription', 'Search top operations');
|
||||
export const searchPlaceholder = localize('ep.searchPlaceholder', 'Filter for any field...');
|
||||
|
||||
@@ -352,7 +352,7 @@ export class ExecutionPlanComparisonEditorView {
|
||||
|
||||
if (this.activeBottomPlanDiagram) {
|
||||
const element = this.activeBottomPlanDiagram.getElementById(`element-` + similarNode.matchingNodesId[0]);
|
||||
if (this.activeBottomPlanDiagram.getSelectedElement() && similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||
if (similarNode.matchingNodesId.find(m => this.activeBottomPlanDiagram.getSelectedElement().id === `element-` + m) !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { InternalExecutionPlanElement } from 'sql/workbench/contrib/executionPla
|
||||
import { executionPlanComparisonPropertiesDifferent, executionPlanComparisonPropertiesUpArrow, executionPlanComparisonPropertiesDownArrow } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { TextWithIconColumn } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export enum ExecutionPlanCompareOrientation {
|
||||
@@ -56,9 +56,10 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
parentContainer: HTMLElement,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextViewService contextViewService: IContextViewService
|
||||
) {
|
||||
super(parentContainer, themeService, instantiationService, contextMenuService);
|
||||
super(parentContainer, themeService, instantiationService, contextMenuService, contextViewService);
|
||||
this._model = <ExecutionPlanComparisonPropertiesViewModel>{};
|
||||
this._parentContainer.style.display = 'none';
|
||||
const header = DOM.$('.compare-operation-name');
|
||||
@@ -133,8 +134,7 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
if (this._model.secondaryElement?.properties) {
|
||||
secondaryProps = this._model.secondaryElement.properties;
|
||||
}
|
||||
|
||||
this.populateTable(columns, this.convertPropertiesToTableRows(primaryProps, secondaryProps, -1, 0));
|
||||
this.populateTable(columns, this.convertPropertiesToTableRows(primaryProps, secondaryProps));
|
||||
}
|
||||
|
||||
private getPropertyTableColumns() {
|
||||
@@ -212,7 +212,8 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
}));
|
||||
}
|
||||
|
||||
private convertPropertiesToTableRows(primaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[], secondaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[], parentIndex: number, indent: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
||||
private convertPropertiesToTableRows(primaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[], secondaryNode: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): { [key: string]: string }[] {
|
||||
const rows: { [key: string]: string }[] = [];
|
||||
let propertiesMap: Map<string, TablePropertiesMapEntry> = new Map();
|
||||
|
||||
if (primaryNode) {
|
||||
@@ -258,7 +259,6 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
row['name'] = {
|
||||
text: k
|
||||
};
|
||||
row['parent'] = parentIndex;
|
||||
|
||||
const primaryProp = v.primaryProp;
|
||||
const secondaryProp = v.secondaryProp;
|
||||
@@ -294,13 +294,10 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
row['secondary'].iconCssClass += ` parent-row-styling`;
|
||||
}
|
||||
rows.push(row);
|
||||
if (!isString(primaryProp.value) && !isString(secondaryProp.value)) {
|
||||
this.convertPropertiesToTableRows(primaryProp.value, secondaryProp.value, rows.length - 1, indent + 2, rows);
|
||||
} else if (isString(primaryProp?.value) && !isString(secondaryProp.value)) {
|
||||
this.convertPropertiesToTableRows(undefined, secondaryProp.value, rows.length - 1, indent + 2, rows);
|
||||
} else if (!isString(primaryProp.value) && !isString(secondaryProp.value)) {
|
||||
this.convertPropertiesToTableRows(primaryProp.value, undefined, rows.length - 1, indent + 2, rows);
|
||||
}
|
||||
const topPropValue = isString(primaryProp.value) ? undefined : primaryProp.value;
|
||||
const bottomPropValue = isString(secondaryProp.value) ? undefined : secondaryProp.value;
|
||||
row['treeGridChildren'] = this.convertPropertiesToTableRows(topPropValue, bottomPropValue);
|
||||
|
||||
} else if (primaryProp && !secondaryProp) {
|
||||
row['displayOrder'] = v.primaryProp.displayOrder;
|
||||
row['primary'] = {
|
||||
@@ -310,7 +307,7 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
if (!isString(primaryProp.value)) {
|
||||
row['name'].iconCssClass += ` parent-row-styling`;
|
||||
row['primary'].iconCssClass += ` parent-row-styling`;
|
||||
this.convertPropertiesToTableRows(primaryProp.value, undefined, rows.length - 1, indent + 2, rows);
|
||||
row['treeGridChildren'] = this.convertPropertiesToTableRows(primaryProp.value, undefined);
|
||||
}
|
||||
} else if (!primaryProp && secondaryProp) {
|
||||
row['displayOrder'] = v.secondaryProp.displayOrder;
|
||||
@@ -322,12 +319,11 @@ export class ExecutionPlanComparisonPropertiesView extends ExecutionPlanProperti
|
||||
if (!isString(secondaryProp.value)) {
|
||||
row['name'].iconCssClass += ` parent-row-styling`;
|
||||
row['secondary'].iconCssClass += ` parent-row-styling`;
|
||||
this.convertPropertiesToTableRows(undefined, secondaryProp.value, rows.length - 1, indent + 2, rows);
|
||||
row['treeGridChildren'] = this.convertPropertiesToTableRows(undefined, secondaryProp.value);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { removeLineBreaks } from 'sql/base/common/strings';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { textFormatter } from 'sql/base/browser/ui/table/formatters';
|
||||
import { ExecutionPlanPropertiesViewBase, PropertiesSortType } from 'sql/workbench/contrib/executionPlan/browser/executionPlanPropertiesViewBase';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase {
|
||||
@@ -24,8 +24,9 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IContextViewService contextViewService: IContextViewService
|
||||
) {
|
||||
super(parentContainer, themeService, instantiationService, contextMenuService);
|
||||
super(parentContainer, themeService, instantiationService, contextMenuService, contextViewService);
|
||||
this._model = <ExecutionPlanPropertiesView>{};
|
||||
this._operationName = DOM.$('h3');
|
||||
this._operationName.classList.add('operation-name');
|
||||
@@ -108,14 +109,38 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
}
|
||||
];
|
||||
|
||||
this.populateTable(columns, this.convertModelToTableRows(this._model.graphElement.properties, -1, 0));
|
||||
this.populateTable(columns, this.convertPropertiesToTableRows(this._model.graphElement?.properties));
|
||||
}
|
||||
|
||||
private convertModelToTableRows(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[], parentIndex: number, indent: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
||||
if (!props) {
|
||||
return rows;
|
||||
private convertPropertiesToTableRows(properties: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): Slick.SlickData[] {
|
||||
if (!properties) {
|
||||
return [];
|
||||
}
|
||||
const sortedProperties = this.sortProperties(properties);
|
||||
const rows: Slick.SlickData[] = [];
|
||||
sortedProperties.forEach((property, index) => {
|
||||
let row = {};
|
||||
row['name'] = property.name;
|
||||
if (!isString(property.value)) {
|
||||
// Styling values in the parent row differently to make them more apparent and standout compared to the rest of the cells.
|
||||
row['name'] = {
|
||||
text: row['name']
|
||||
};
|
||||
row['value'] = {
|
||||
text: removeLineBreaks(property.displayValue, ' ')
|
||||
};
|
||||
row['tootltip'] = property.displayValue;
|
||||
row['treeGridChildren'] = this.convertPropertiesToTableRows(property.value);
|
||||
} else {
|
||||
row['value'] = removeLineBreaks(property.displayValue, ' ');
|
||||
row['tooltip'] = property.displayValue;
|
||||
}
|
||||
rows.push(row);
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
|
||||
private sortProperties(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[]): azdata.executionPlan.ExecutionPlanGraphElementProperty[] {
|
||||
switch (this.sortType) {
|
||||
case PropertiesSortType.DisplayOrder:
|
||||
props = this.sortPropertiesByImportance(props);
|
||||
@@ -127,6 +152,13 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
props = this.sortPropertiesReverseAlphabetically(props);
|
||||
break;
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
private convertModelToTableRows(props: azdata.executionPlan.ExecutionPlanGraphElementProperty[] | undefined, parentIndex: number, rows: { [key: string]: string }[] = []): { [key: string]: string }[] {
|
||||
if (!props) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
props.forEach((p, i) => {
|
||||
let row = {};
|
||||
@@ -142,9 +174,11 @@ export class ExecutionPlanPropertiesView extends ExecutionPlanPropertiesViewBase
|
||||
text: removeLineBreaks(p.displayValue, ' ')
|
||||
};
|
||||
row['tootltip'] = p.displayValue;
|
||||
this.convertModelToTableRows(p.value, rows.length - 1, indent + 2, rows);
|
||||
this.convertModelToTableRows(p.value, rows.length - 1, rows);
|
||||
} else {
|
||||
row['value'] = removeLineBreaks(p.displayValue, ' ');
|
||||
row['value'] = {
|
||||
text: removeLineBreaks(p.displayValue, ' ')
|
||||
};
|
||||
row['tooltip'] = p.displayValue;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,26 +10,36 @@ import { localize } from 'vs/nls';
|
||||
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { sortAlphabeticallyIconClassNames, sortByDisplayOrderIconClassNames, sortReverseAlphabeticallyIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import { propertiesSearchDescription, searchIconClassNames, searchPlaceholder, sortAlphabeticallyIconClassNames, sortByDisplayOrderIconClassNames, sortReverseAlphabeticallyIconClassNames } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
import { attachTableStyler } from 'sql/platform/theme/common/styler';
|
||||
import { RESULTS_GRID_DEFAULTS } from 'sql/workbench/common/constants';
|
||||
import { contrastBorder, listHoverBackground, listInactiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { contrastBorder, inputBackground, listHoverBackground, listInactiveSelectionBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { TreeGrid } from 'sql/base/browser/ui/table/treeGrid';
|
||||
import { ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
|
||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { isString } from 'vs/base/common/types';
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLayoutProvider {
|
||||
export abstract class ExecutionPlanPropertiesViewBase extends Disposable implements IVerticalSashLayoutProvider {
|
||||
// Title bar with close button action
|
||||
private _titleBarContainer!: HTMLElement;
|
||||
private _titleBarTextContainer!: HTMLElement;
|
||||
private _titleBarActionsContainer!: HTMLElement;
|
||||
private _titleActions: ActionBar;
|
||||
|
||||
|
||||
// search bar and functrion
|
||||
private _propertiesSearchInput: InputBox;
|
||||
private _propertiesSearchInputContainer: HTMLElement;
|
||||
|
||||
private _searchAndActionBarContainer: HTMLElement;
|
||||
|
||||
// Header container
|
||||
private _headerContainer: HTMLElement;
|
||||
|
||||
@@ -50,22 +60,25 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
|
||||
private _selectionModel: CellSelectionModel<Slick.SlickData>;
|
||||
|
||||
private _tableData: Slick.SlickData[];
|
||||
|
||||
constructor(
|
||||
public _parentContainer: HTMLElement,
|
||||
private _themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||
@IContextViewService private _contextViewService: IContextViewService
|
||||
) {
|
||||
|
||||
super();
|
||||
const sashContainer = DOM.$('.properties-sash');
|
||||
this._parentContainer.appendChild(sashContainer);
|
||||
|
||||
this.resizeSash = new Sash(sashContainer, this, { orientation: Orientation.VERTICAL, size: 3 });
|
||||
this.resizeSash = this._register(new Sash(sashContainer, this, { orientation: Orientation.VERTICAL, size: 3 }));
|
||||
let originalWidth = 0;
|
||||
this.resizeSash.onDidStart((e: ISashEvent) => {
|
||||
this._register(this.resizeSash.onDidStart((e: ISashEvent) => {
|
||||
originalWidth = this._parentContainer.clientWidth;
|
||||
});
|
||||
this.resizeSash.onDidChange((evt: ISashEvent) => {
|
||||
}));
|
||||
this._register(this.resizeSash.onDidChange((evt: ISashEvent) => {
|
||||
const change = evt.startX - evt.currentX;
|
||||
const newWidth = originalWidth + change;
|
||||
if (newWidth < 200) {
|
||||
@@ -73,9 +86,7 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
}
|
||||
this._parentContainer.style.flex = `0 0 ${newWidth}px`;
|
||||
propertiesContent.style.width = `${newWidth}px`;
|
||||
});
|
||||
this.resizeSash.onDidEnd(() => {
|
||||
});
|
||||
}));
|
||||
|
||||
const propertiesContent = DOM.$('.properties-content');
|
||||
this._parentContainer.appendChild(propertiesContent);
|
||||
@@ -91,21 +102,35 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
|
||||
this._titleBarActionsContainer = DOM.$('.action-bar');
|
||||
this._titleBarContainer.appendChild(this._titleBarActionsContainer);
|
||||
this._titleActions = new ActionBar(this._titleBarActionsContainer, {
|
||||
this._titleActions = this._register(new ActionBar(this._titleBarActionsContainer, {
|
||||
orientation: ActionsOrientation.HORIZONTAL, context: this
|
||||
});
|
||||
}));
|
||||
this._titleActions.pushAction([new ClosePropertyViewAction()], { icon: true, label: false });
|
||||
|
||||
this._headerContainer = DOM.$('.header');
|
||||
propertiesContent.appendChild(this._headerContainer);
|
||||
|
||||
this._searchAndActionBarContainer = DOM.$('.search-action-bar-container');
|
||||
propertiesContent.appendChild(this._searchAndActionBarContainer);
|
||||
|
||||
this._headerActionsContainer = DOM.$('.table-action-bar');
|
||||
propertiesContent.appendChild(this._headerActionsContainer);
|
||||
this._headerActions = new ActionBar(this._headerActionsContainer, {
|
||||
this._searchAndActionBarContainer.appendChild(this._headerActionsContainer);
|
||||
this._headerActions = this._register(new ActionBar(this._headerActionsContainer, {
|
||||
orientation: ActionsOrientation.HORIZONTAL, context: this
|
||||
});
|
||||
}));
|
||||
this._headerActions.pushAction([new SortPropertiesByDisplayOrderAction(), new SortPropertiesAlphabeticallyAction(), new SortPropertiesReverseAlphabeticallyAction()], { icon: true, label: false });
|
||||
|
||||
this._propertiesSearchInputContainer = DOM.$('.table-search');
|
||||
this._propertiesSearchInputContainer.classList.add('codicon', searchIconClassNames);
|
||||
this._propertiesSearchInput = this._register(new InputBox(this._propertiesSearchInputContainer, this._contextViewService, {
|
||||
ariaDescription: propertiesSearchDescription,
|
||||
placeholder: searchPlaceholder
|
||||
}));
|
||||
this._propertiesSearchInput.element.classList.add('codicon', searchIconClassNames);
|
||||
this._searchAndActionBarContainer.appendChild(this._propertiesSearchInputContainer);
|
||||
this._register(this._propertiesSearchInput.onDidChange(e => {
|
||||
this.searchTable(e);
|
||||
}));
|
||||
|
||||
this._tableContainer = DOM.$('.table-container');
|
||||
propertiesContent.appendChild(this._tableContainer);
|
||||
@@ -115,7 +140,7 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
|
||||
this._selectionModel = new CellSelectionModel<Slick.SlickData>();
|
||||
|
||||
this._tableComponent = new TreeGrid(table, {
|
||||
this._tableComponent = this._register(new TreeGrid(table, {
|
||||
columns: []
|
||||
}, {
|
||||
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
||||
@@ -123,7 +148,7 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
defaultColumnWidth: 120,
|
||||
editable: true,
|
||||
autoEdit: false
|
||||
});
|
||||
}));
|
||||
attachTableStyler(this._tableComponent, this._themeService);
|
||||
this._tableComponent.setSelectionModel(this._selectionModel);
|
||||
|
||||
@@ -131,20 +156,20 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
this._instantiationService.createInstance(CopyTableData),
|
||||
];
|
||||
|
||||
this._tableComponent.onContextMenu(e => {
|
||||
this._register(this._tableComponent.onContextMenu(e => {
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => contextMenuAction,
|
||||
getActionsContext: () => this.getCopyString()
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
let copyHandler = new CopyKeybind<any>();
|
||||
this._tableComponent.registerPlugin(copyHandler);
|
||||
|
||||
copyHandler.onCopy(e => {
|
||||
this._register(copyHandler.onCopy(e => {
|
||||
this._instantiationService.createInstance(CopyTableData).run(this.getCopyString());
|
||||
});
|
||||
}));
|
||||
|
||||
new ResizeObserver((e) => {
|
||||
this.resizeSash.layout();
|
||||
@@ -226,7 +251,9 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
public populateTable(columns: Slick.Column<Slick.SlickData>[], data: { [key: string]: string }[]) {
|
||||
this._tableComponent.columns = columns;
|
||||
this._tableContainer.scrollTo(0, 0);
|
||||
this._tableComponent.setData(data);
|
||||
this._tableData = data;
|
||||
this._propertiesSearchInput.value = '';
|
||||
this._tableComponent.setData(this.flattenTableData(data, -1));
|
||||
this.resizeTable();
|
||||
}
|
||||
|
||||
@@ -245,6 +272,68 @@ export abstract class ExecutionPlanPropertiesViewBase implements IVerticalSashLa
|
||||
this._tableComponent.resizeCanvas();
|
||||
}
|
||||
|
||||
private searchTable(searchString: string): void {
|
||||
if (searchString === '') {
|
||||
this._tableComponent.setData(this.flattenTableData(this._tableData, -1));
|
||||
} else {
|
||||
this._tableComponent.setData(
|
||||
this.flattenTableData(
|
||||
this.searchNestedTableData(searchString, this._tableData).data,
|
||||
-1)
|
||||
);
|
||||
}
|
||||
this._tableComponent.rerenderGrid();
|
||||
}
|
||||
|
||||
private searchNestedTableData(search: string, data: Slick.SlickData[]): { include: boolean, data: Slick.SlickData[] } {
|
||||
let resultData: Slick.SlickData[] = [];
|
||||
data.forEach(dataRow => {
|
||||
let includeRow = false;
|
||||
const columns = this._tableComponent.grid.getColumns();
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
let dataValue = '';
|
||||
let rawDataValue = dataRow[columns[i].field];
|
||||
if (isString(rawDataValue)) {
|
||||
dataValue = rawDataValue;
|
||||
} else if (rawDataValue !== undefined) {
|
||||
dataValue = rawDataValue.text ?? rawDataValue.title;
|
||||
}
|
||||
if (dataValue.toLowerCase().includes(search.toLowerCase())) {
|
||||
includeRow = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const rowClone = deepClone(dataRow);
|
||||
if (rowClone['treeGridChildren'] !== undefined) {
|
||||
const result = this.searchNestedTableData(search, rowClone['treeGridChildren']);
|
||||
rowClone['treeGridChildren'] = result.data;
|
||||
includeRow = includeRow || result.include;
|
||||
}
|
||||
|
||||
if (includeRow) {
|
||||
if (rowClone['treeGridChildren'] !== undefined) {
|
||||
rowClone['expanded'] = true;
|
||||
}
|
||||
resultData.push(rowClone);
|
||||
}
|
||||
});
|
||||
return { include: resultData.length > 0, data: resultData };
|
||||
}
|
||||
|
||||
private flattenTableData(nestedData: Slick.SlickData[], parentIndex: number, rows: Slick.SlickData[] = []): Slick.SlickData[] {
|
||||
if (nestedData === undefined || nestedData.length === 0) {
|
||||
return rows;
|
||||
}
|
||||
nestedData.forEach((dataRow) => {
|
||||
rows.push(dataRow);
|
||||
dataRow['parent'] = parentIndex;
|
||||
if (dataRow['treeGridChildren']) {
|
||||
this.flattenTableData(dataRow['treeGridChildren'], rows.length - 1, rows);
|
||||
}
|
||||
});
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -342,6 +431,15 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
const searchBarBackground = theme.getColor(inputBackground);
|
||||
if (inputBackground) {
|
||||
collector.addRule(`
|
||||
.eps-container .properties .search-action-bar-container .table-search {
|
||||
background-color: ${searchBarBackground};
|
||||
}
|
||||
`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -160,10 +160,12 @@ export class ExecutionPlanView implements ISashLayoutProvider {
|
||||
new SavePlanFile(),
|
||||
new OpenPlanFile(),
|
||||
this._instantiationService.createInstance(OpenQueryAction, 'ActionBar'),
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(ZoomInAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(ZoomOutAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(ZoomToFitAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(CustomZoomAction, 'ActionBar'),
|
||||
new Separator(),
|
||||
this._instantiationService.createInstance(SearchNodeAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(PropertiesAction, 'ActionBar'),
|
||||
this._instantiationService.createInstance(CompareExecutionPlanAction, 'ActionBar'),
|
||||
|
||||
@@ -1,23 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 16 16"
|
||||
style="enable-background:new 0 0 16 16;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="customZoom.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
id="namedview11"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.75"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="1792"
|
||||
inkscape:window-height="1049"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#F6F6F6;fill-opacity:0;}
|
||||
.st1{fill:#F6F6F6;}
|
||||
.st2{fill:#424242;}
|
||||
.st3{fill:#F0EFF1;}
|
||||
</style>
|
||||
<g id="canvas">
|
||||
<path class="st0" d="M16,16H0V0h16V16z"/>
|
||||
<g
|
||||
id="canvas"
|
||||
transform="matrix(-1,0,0,1,15.95,0)">
|
||||
<path
|
||||
class="st0"
|
||||
d="M 16,16 H 0 V 0 h 16 z"
|
||||
id="path4" />
|
||||
</g>
|
||||
<path id="outline" class="st1" d="M16,5.8c0-3.2-2.6-5.7-5.8-5.8C7,0,4.4,2.6,4.3,5.8c0,1,0.3,2,0.8,2.9l-4.7,4.7
|
||||
c-0.6,0.6-0.6,1.5,0,2.1c0.6,0.6,1.5,0.6,2.1,0l4.7-4.7c0.4,0.3,0.9,0.4,1.3,0.6l-0.8,0.8L7,16h1.4l2.4-0.8l5.2-5.1l-1-1
|
||||
C15.6,8.1,16,7,16,5.8z"/>
|
||||
<path id="iconBg" class="st2" d="M10.4,9.6c-0.1,0-0.2,0-0.2,0C8,9.7,6.3,8,6.3,5.8C6.3,3.7,8,2,10.2,2C12.3,2,14,3.7,14,5.8v0
|
||||
c0,0.6-0.2,1.2-0.5,1.8l0.7,0.7c0.4-0.7,0.7-1.6,0.7-2.5c0-2.6-2.1-4.7-4.8-4.8v0C7.5,1,5.3,3.2,5.3,5.8c0,1.1,0.4,2.2,1.1,3
|
||||
l-5.3,5.3c-0.2,0.2-0.2,0.5,0,0.7c0.2,0.2,0.5,0.2,0.7,0l5.3-5.3c0.7,0.6,1.5,0.9,2.3,1L10.4,9.6z M8.2,15l2.1-0.7l-1.6-1.6L8.2,15z
|
||||
M12.3,9.2l1.6,1.6l0.7-0.7L13,8.5L12.3,9.2z M9.5,12l1.6,1.6l2.1-2.1l-1.6-1.6L9.5,12z"/>
|
||||
<path id="iconFg" class="st3" d="M10.2,9.7c0.1,0,0.2,0,0.2,0L13,7.1l0.6,0.6C13.8,7.1,14,6.5,14,5.8C14,3.7,12.3,2,10.2,2
|
||||
c0,0,0,0,0,0C8,2,6.3,3.7,6.3,5.8C6.3,8,8,9.7,10.2,9.7z"/>
|
||||
<path
|
||||
id="outline"
|
||||
class="st1"
|
||||
d="m -0.05,5.8 c 0,-3.2 2.6,-5.7 5.8,-5.8 3.2,0 5.8,2.6 5.9,5.8 0,1 -0.3,2 -0.8,2.9 l 4.7,4.7 c 0.6,0.6 0.6,1.5 0,2.1 -0.6,0.6 -1.5,0.6 -2.1,0 l -4.7,-4.7 c -0.4,0.3 -0.9,0.4 -1.3,0.6 l 0.8,0.8 0.7,3.8 h -1.4 l -2.4,-0.8 -5.2,-5.1 1,-1 c -0.6,-1 -1,-2.1 -1,-3.3 z" />
|
||||
<path
|
||||
id="iconBg"
|
||||
class="st2"
|
||||
d="m 5.55,9.6 c 0.1,0 0.2,0 0.2,0 C 7.95,9.7 9.65,8 9.65,5.8 9.65,3.7 7.95,2 5.75,2 3.65,2 1.95,3.7 1.95,5.8 v 0 c 0,0.6 0.2,1.2 0.5,1.8 L 1.75,8.3 C 1.35,7.6 1.05,6.7 1.05,5.8 1.05,3.2 3.15,1.1 5.85,1 v 0 c 2.6,0 4.8,2.2 4.8,4.8 0,1.1 -0.4,2.2 -1.1,3 l 5.3,5.3 c 0.2,0.2 0.2,0.5 0,0.7 -0.2,0.2 -0.5,0.2 -0.7,0 L 8.85,9.5 c -0.7,0.6 -1.5,0.9 -2.3,1 z m 2.2,5.4 -2.1,-0.7 1.6,-1.6 z m -4.1,-5.8 -1.6,1.6 -0.7,-0.7 1.6,-1.6 z m 2.8,2.8 -1.6,1.6 -2.1,-2.1 1.6,-1.6 z" />
|
||||
<path
|
||||
id="iconFg"
|
||||
class="st3"
|
||||
d="m 5.75,9.7 c -0.1,0 -0.2,0 -0.2,0 L 2.95,7.1 2.35,7.7 C 2.15,7.1 1.95,6.5 1.95,5.8 1.95,3.7 3.65,2 5.75,2 c 0,0 0,0 0,0 2.2,0 3.9,1.7 3.9,3.8 0,2.2 -1.7,3.9 -3.9,3.9 z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -1,23 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 16 16"
|
||||
style="enable-background:new 0 0 16 16;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="customZoomDark.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs148" /><sodipodi:namedview
|
||||
id="namedview146"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="14.75"
|
||||
inkscape:cx="8"
|
||||
inkscape:cy="8"
|
||||
inkscape:window-width="1792"
|
||||
inkscape:window-height="1049"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style137">
|
||||
.st0{fill:#F6F6F6;fill-opacity:0;}
|
||||
.st1{fill:#0000;}
|
||||
.st2{fill:#fff;}
|
||||
.st3{fill:#0000;}
|
||||
</style>
|
||||
<g id="canvas">
|
||||
<path class="st0" d="M16,16H0V0h16V16z"/>
|
||||
<g
|
||||
id="canvas"
|
||||
transform="matrix(-1,0,0,1,15.95,0)">
|
||||
<path
|
||||
class="st0"
|
||||
d="M 16,16 H 0 V 0 h 16 z"
|
||||
id="path139" />
|
||||
</g>
|
||||
<path id="outline" class="st1" d="M16,5.8c0-3.2-2.6-5.7-5.8-5.8C7,0,4.4,2.6,4.3,5.8c0,1,0.3,2,0.8,2.9l-4.7,4.7
|
||||
c-0.6,0.6-0.6,1.5,0,2.1c0.6,0.6,1.5,0.6,2.1,0l4.7-4.7c0.4,0.3,0.9,0.4,1.3,0.6l-0.8,0.8L7,16h1.4l2.4-0.8l5.2-5.1l-1-1
|
||||
C15.6,8.1,16,7,16,5.8z"/>
|
||||
<path id="iconBg" class="st2" d="M10.4,9.6c-0.1,0-0.2,0-0.2,0C8,9.7,6.3,8,6.3,5.8C6.3,3.7,8,2,10.2,2C12.3,2,14,3.7,14,5.8v0
|
||||
c0,0.6-0.2,1.2-0.5,1.8l0.7,0.7c0.4-0.7,0.7-1.6,0.7-2.5c0-2.6-2.1-4.7-4.8-4.8v0C7.5,1,5.3,3.2,5.3,5.8c0,1.1,0.4,2.2,1.1,3
|
||||
l-5.3,5.3c-0.2,0.2-0.2,0.5,0,0.7c0.2,0.2,0.5,0.2,0.7,0l5.3-5.3c0.7,0.6,1.5,0.9,2.3,1L10.4,9.6z M8.2,15l2.1-0.7l-1.6-1.6L8.2,15z
|
||||
M12.3,9.2l1.6,1.6l0.7-0.7L13,8.5L12.3,9.2z M9.5,12l1.6,1.6l2.1-2.1l-1.6-1.6L9.5,12z"/>
|
||||
<path id="iconFg" class="st3" d="M10.2,9.7c0.1,0,0.2,0,0.2,0L13,7.1l0.6,0.6C13.8,7.1,14,6.5,14,5.8C14,3.7,12.3,2,10.2,2
|
||||
c0,0,0,0,0,0C8,2,6.3,3.7,6.3,5.8C6.3,8,8,9.7,10.2,9.7z"/>
|
||||
<path
|
||||
id="outline"
|
||||
class="st1"
|
||||
d="m -0.05,5.8 c 0,-3.2 2.6,-5.7 5.8,-5.8 3.2,0 5.8,2.6 5.9,5.8 0,1 -0.3,2 -0.8,2.9 l 4.7,4.7 c 0.6,0.6 0.6,1.5 0,2.1 -0.6,0.6 -1.5,0.6 -2.1,0 l -4.7,-4.7 c -0.4,0.3 -0.9,0.4 -1.3,0.6 l 0.8,0.8 0.7,3.8 h -1.4 l -2.4,-0.8 -5.2,-5.1 1,-1 c -0.6,-1 -1,-2.1 -1,-3.3 z" />
|
||||
<path
|
||||
id="iconBg"
|
||||
class="st2"
|
||||
d="m 5.55,9.6 c 0.1,0 0.2,0 0.2,0 C 7.95,9.7 9.65,8 9.65,5.8 9.65,3.7 7.95,2 5.75,2 3.65,2 1.95,3.7 1.95,5.8 v 0 c 0,0.6 0.2,1.2 0.5,1.8 L 1.75,8.3 C 1.35,7.6 1.05,6.7 1.05,5.8 1.05,3.2 3.15,1.1 5.85,1 v 0 c 2.6,0 4.8,2.2 4.8,4.8 0,1.1 -0.4,2.2 -1.1,3 l 5.3,5.3 c 0.2,0.2 0.2,0.5 0,0.7 -0.2,0.2 -0.5,0.2 -0.7,0 L 8.85,9.5 c -0.7,0.6 -1.5,0.9 -2.3,1 z m 2.2,5.4 -2.1,-0.7 1.6,-1.6 z m -4.1,-5.8 -1.6,1.6 -0.7,-0.7 1.6,-1.6 z m 2.8,2.8 -1.6,1.6 -2.1,-2.1 1.6,-1.6 z" />
|
||||
<path
|
||||
id="iconFg"
|
||||
class="st3"
|
||||
d="m 5.75,9.7 c -0.1,0 -0.2,0 -0.2,0 L 2.95,7.1 2.35,7.7 C 2.15,7.1 1.95,6.5 1.95,5.8 1.95,3.7 3.65,2 5.75,2 c 0,0 0,0 0,0 2.2,0 3.9,1.7 3.9,3.8 0,2.2 -1.7,3.9 -3.9,3.9 z" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -101,7 +101,7 @@ However we always want it to be the width of the container it is resizing.
|
||||
|
||||
/* execution plan header that contains the relative query cost, query statement and recommendations */
|
||||
.eps-container .execution-plan .plan .header .query-row,
|
||||
.top-operations-tab .top-operations-container .query-row{
|
||||
.top-operations-tab .top-operations-container .query-row {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
@@ -187,6 +187,31 @@ However we always want it to be the width of the container it is resizing.
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.eps-container .properties .search-action-bar-container .table-action-bar {
|
||||
flex: auto
|
||||
}
|
||||
|
||||
.eps-container .properties .search-action-bar-container .table-search {
|
||||
flex: 1;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.eps-container .properties .search-action-bar-container .table-search>div,
|
||||
.top-operations-tab .top-operations-container .top-operations-header-search-bar>div {
|
||||
background-position-y: center;
|
||||
background-position-x: 3px;
|
||||
}
|
||||
|
||||
.eps-container .properties .search-action-bar-container .table-search input,
|
||||
.top-operations-tab .top-operations-container .top-operations-header-search-bar input {
|
||||
margin-left: 20px;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.eps-container .properties .search-action-bar-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Operation name styling in the properties view. */
|
||||
.eps-container .properties .operation-name {
|
||||
white-space: nowrap;
|
||||
@@ -316,7 +341,8 @@ However we always want it to be the width of the container it is resizing.
|
||||
}
|
||||
|
||||
|
||||
.eps-container .ep-search-icon {
|
||||
.eps-container .ep-search-icon,
|
||||
.top-operations-tab .ep-search-icon {
|
||||
background-image: url(../images/actionIcons/search.svg);
|
||||
background-size: 16px 16px;
|
||||
background-position: center;
|
||||
@@ -324,7 +350,9 @@ However we always want it to be the width of the container it is resizing.
|
||||
}
|
||||
|
||||
.vs-dark .eps-container .ep-search-icon,
|
||||
.hc-black .eps-container .ep-search-icon {
|
||||
.hc-black .eps-container .ep-search-icon,
|
||||
.vs-dark .top-operations-tab .ep-search-icon,
|
||||
.hc-black .top-operations-tab .ep-search-icon {
|
||||
background-image: url(../images/actionIcons/searchDark.svg);
|
||||
background-size: 16px 16px;
|
||||
background-position: center;
|
||||
@@ -799,21 +827,34 @@ However we always want it to be the width of the container it is resizing.
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.top-operations-tab .top-operations-container .top-operations-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.top-operations-tab .top-operations-container .top-operations-header .top-operations-header-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.top-operations-tab .top-operations-container .top-operations-header .top-operations-header-search-bar {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.top-operations-tab .top-operations-container .table-container {
|
||||
flex: 1;
|
||||
height: calc(100% - 50px);
|
||||
}
|
||||
|
||||
.graph-cell{
|
||||
.graph-cell {
|
||||
align-items: center;
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
height: 80px;
|
||||
font-size: 10px;
|
||||
font-family:'Monaco', 'Menlo', 'Consolas';
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas';
|
||||
}
|
||||
|
||||
.graph-cell-body{
|
||||
.graph-cell-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -822,12 +863,12 @@ However we always want it to be the width of the container it is resizing.
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.graph-cell-icon{
|
||||
.graph-cell-icon {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
width:30px;
|
||||
height:30px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-self: center;
|
||||
position: relative;
|
||||
}
|
||||
@@ -837,8 +878,8 @@ However we always want it to be the width of the container it is resizing.
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
width:12px;
|
||||
height:12px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
bottom: 0;
|
||||
right: -5px;
|
||||
}
|
||||
@@ -846,9 +887,10 @@ However we always want it to be the width of the container it is resizing.
|
||||
.graph-cell .graph-cell-row-count {
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
left: calc(50% - 60px);
|
||||
right: calc(50% + 17px);
|
||||
margin-right: 3px;
|
||||
min-width: 20px;
|
||||
text-align: right;
|
||||
text-align: left;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
@@ -861,7 +903,7 @@ However we always want it to be the width of the container it is resizing.
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.graph-cell-cost{
|
||||
.graph-cell-cost {
|
||||
border-radius: 15px;
|
||||
width: fit-content;
|
||||
padding: 1px 8px 0px 8px;
|
||||
|
||||
@@ -23,11 +23,13 @@ import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugi
|
||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||
import * as sqlExtHostType from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { listHoverBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ITableKeyboardEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { searchIconClassNames, searchPlaceholder, topOperationsSearchDescription } from 'sql/workbench/contrib/executionPlan/browser/constants';
|
||||
|
||||
const TABLE_SORT_COLUMN_KEY = 'tableCostColumnForSorting';
|
||||
|
||||
@@ -59,6 +61,7 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
||||
@IThemeService private _themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||
@IContextViewService private _contextViewService: IContextViewService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -145,13 +148,30 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
||||
name: c,
|
||||
field: c.toString(),
|
||||
formatter: i === 0 ? hyperLinkFormatter : textFormatter,
|
||||
sortable: true,
|
||||
sortable: true
|
||||
};
|
||||
});
|
||||
|
||||
const topOperationContainer = DOM.$('.top-operations-container');
|
||||
this._container.appendChild(topOperationContainer);
|
||||
const header = this._instantiationService.createInstance(ExecutionPlanViewHeader, topOperationContainer, {
|
||||
|
||||
const headerContainer = DOM.$('.top-operations-header');
|
||||
topOperationContainer.appendChild(headerContainer);
|
||||
|
||||
const headerInfoContainer = DOM.$('.top-operations-header-info');
|
||||
headerContainer.appendChild(headerInfoContainer);
|
||||
|
||||
const headerSearchBarContainer = DOM.$('.top-operations-header-search-bar');
|
||||
headerContainer.appendChild(headerSearchBarContainer);
|
||||
headerContainer.classList.add('codicon', searchIconClassNames);
|
||||
|
||||
const topOperationsSearchInput = new InputBox(headerSearchBarContainer, this._contextViewService, {
|
||||
ariaDescription: topOperationsSearchDescription,
|
||||
placeholder: searchPlaceholder
|
||||
});
|
||||
topOperationsSearchInput.element.classList.add('codicon', searchIconClassNames);
|
||||
|
||||
const header = this._instantiationService.createInstance(ExecutionPlanViewHeader, headerInfoContainer, {
|
||||
planIndex: index,
|
||||
});
|
||||
header.query = graph.query;
|
||||
@@ -207,7 +227,7 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
||||
columns: columns,
|
||||
sorter: (args) => {
|
||||
const column = args.sortCol.field;
|
||||
dataMap.sort((a, b) => {
|
||||
const sortedData = table.getData().getItems().sort((a, b) => {
|
||||
let result = -1;
|
||||
|
||||
if (!a[column]) {
|
||||
@@ -237,7 +257,7 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
||||
}
|
||||
return args.sortAsc ? result : -result;
|
||||
});
|
||||
table.setData(dataMap);
|
||||
table.setData(sortedData);
|
||||
}
|
||||
}, {
|
||||
rowHeight: RESULTS_GRID_DEFAULTS.rowHeight,
|
||||
@@ -268,6 +288,29 @@ export class TopOperationsTabView extends Disposable implements IPanelView {
|
||||
this._instantiationService.createInstance(SelectAll)
|
||||
];
|
||||
|
||||
this._register(topOperationsSearchInput.onDidChange(e => {
|
||||
const filter = e.toLowerCase();
|
||||
if (filter) {
|
||||
const filteredData = dataMap.filter(row => {
|
||||
let includeRow = false;
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const columnField = columns[i].field;
|
||||
if (row[columnField]) {
|
||||
const text = row[columnField].displayText ?? row[columnField].text;
|
||||
if (text.toLowerCase().includes(filter)) {
|
||||
includeRow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return includeRow;
|
||||
});
|
||||
table.setData(filteredData);
|
||||
} else {
|
||||
table.setData(dataMap);
|
||||
}
|
||||
table.rerenderGrid();
|
||||
}));
|
||||
|
||||
this._register(table.onKeyDown((evt: ITableKeyboardEvent) => {
|
||||
if (evt.event.ctrlKey && (evt.event.key === 'a' || evt.event.key === 'A')) {
|
||||
selectionModel.setSelectedRanges([new Slick.Range(0, 1, table.getData().getLength() - 1, table.columns.length - 1)]);
|
||||
|
||||
Reference in New Issue
Block a user