diff --git a/src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts b/src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts index d1a3da36fd..69aed939ff 100644 --- a/src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts @@ -11,6 +11,8 @@ import { addDisposableListener } from 'vs/base/browser/dom'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IDisposableDataProvider } from 'sql/base/common/dataProvider'; +import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { onUnexpectedError } from 'vs/base/common/errors'; export type HeaderFilterCommands = 'sort-asc' | 'sort-desc'; export interface CommandEventArgs { @@ -39,7 +41,7 @@ export class HeaderFilter { private disposableStore = new DisposableStore(); private _enabled: boolean = true; - constructor() { + constructor(private readonly contextViewProvider: IContextViewProvider) { } public init(grid: Slick.Grid): void { @@ -77,10 +79,7 @@ export class HeaderFilter { } private hideMenu() { - if (this.$menu) { - this.$menu.remove(); - this.$menu = undefined; - } + this.contextViewProvider.hideContextView(); } private handleHeaderCellRendered(e: Event, args: Slick.OnHeaderCellRenderedEventArgs) { @@ -172,8 +171,25 @@ export class HeaderFilter { }); } - private async showFilter(element: HTMLElement) { - const target = withNullAsUndefined(element); + private showFilter(filterButton: HTMLElement): void { + this.contextViewProvider.showContextView({ + getAnchor: () => filterButton, + render: (container: HTMLElement) => { + this.renderFilter(filterButton, container).catch(onUnexpectedError); + return { + dispose: () => { + if (this.$menu) { + this.$menu.remove(); + this.$menu = undefined; + } + } + }; + } + }); + } + + private async renderFilter(filterButton: HTMLElement, container: HTMLElement) { + const target = withNullAsUndefined(filterButton); const $menuButton = jQuery(target); this.columnDef = $menuButton.data('column'); @@ -204,7 +220,7 @@ export class HeaderFilter { } if (!this.$menu) { - this.$menu = jQuery('
').appendTo(document.body); + this.$menu = jQuery('
').appendTo(container); } this.$menu.empty(); @@ -265,16 +281,6 @@ export class HeaderFilter { jQuery(':checkbox', $filter).bind('click', (e) => { this.workingFilters = this.changeWorkingFilter(filterItems, this.workingFilters, jQuery(e.target)); }); - - const offset = jQuery(target).offset(); - const left = offset.left - this.$menu.width() + jQuery(target).width() - 8; - - let menutop = offset.top + jQuery(target).height(); - - if (menutop + offset.top > jQuery(window).height()) { - menutop -= (this.$menu.height() + jQuery(target).height() + 8); - } - this.$menu.css('top', menutop).css('left', (left > 0 ? left : 0)); this.okButton.focus(); } diff --git a/src/sql/workbench/browser/modelComponents/table.component.ts b/src/sql/workbench/browser/modelComponents/table.component.ts index c6d04ebd05..4d4469a653 100644 --- a/src/sql/workbench/browser/modelComponents/table.component.ts +++ b/src/sql/workbench/browser/modelComponents/table.component.ts @@ -35,6 +35,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { TableCellClickEventArgs } from 'sql/base/browser/ui/table/plugins/tableColumn'; import { HyperlinkCellValue, HyperlinkColumn } from 'sql/base/browser/ui/table/plugins/hyperlinkColumn.plugin'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; export enum ColumnSizingMode { ForceFit = 0, // all columns will be sized to fit in viewable space, no horiz scroll bar @@ -81,7 +82,8 @@ export default class TableComponent extends ComponentBase ChangeDetectorRef)) changeRef: ChangeDetectorRef, @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, @Inject(forwardRef(() => ElementRef)) el: ElementRef, - @Inject(ILogService) logService: ILogService) { + @Inject(ILogService) logService: ILogService, + @Inject(IContextViewService) private contextViewService: IContextViewService) { super(changeRef, el, logService); } @@ -500,7 +502,7 @@ export default class TableComponent extends ComponentBase(); + const filterPlugin = new HeaderFilter(this.contextViewService); this._register(attachButtonStyler(filterPlugin, this.themeService)); this._filterPlugin = filterPlugin; this._filterPlugin.onFilterApplied.subscribe((e, args) => { diff --git a/src/sql/workbench/contrib/assessment/browser/asmtResultsView.component.ts b/src/sql/workbench/contrib/assessment/browser/asmtResultsView.component.ts index c0e6f45e2d..3865231bbf 100644 --- a/src/sql/workbench/contrib/assessment/browser/asmtResultsView.component.ts +++ b/src/sql/workbench/contrib/assessment/browser/asmtResultsView.component.ts @@ -46,6 +46,7 @@ import { TelemetryView } from 'sql/platform/telemetry/common/telemetryKeys'; import { LocalizedStrings } from 'sql/workbench/contrib/assessment/common/strings'; import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; export const ASMTRESULTSVIEW_SELECTOR: string = 'asmt-results-view-component'; export const ROW_HEIGHT: number = 25; @@ -141,7 +142,8 @@ export class AsmtResultsViewComponent extends TabChild implements IAssessmentCom @Inject(IInstantiationService) private _instantiationService: IInstantiationService, @Inject(IDashboardService) _dashboardService: IDashboardService, @Inject(IAdsTelemetryService) private _telemetryService: IAdsTelemetryService, - @Inject(ILogService) protected _logService: ILogService + @Inject(ILogService) protected _logService: ILogService, + @Inject(IContextViewService) private _contextViewService: IContextViewService ) { super(); let self = this; @@ -316,7 +318,7 @@ export class AsmtResultsViewComponent extends TabChild implements IAssessmentCom columnDef.formatter = (row, cell, value, columnDef, dataContext) => this.detailSelectionFormatter(row, cell, value, columnDef, dataContext as ExtendedItem); columns.unshift(columnDef); - let filterPlugin = new HeaderFilter(); + let filterPlugin = new HeaderFilter(this._contextViewService); this._register(attachButtonStyler(filterPlugin, this._themeService)); this.filterPlugin = filterPlugin; this.filterPlugin.onFilterApplied.subscribe((e, args) => { diff --git a/src/sql/workbench/contrib/jobManagement/browser/jobsView.component.ts b/src/sql/workbench/contrib/jobManagement/browser/jobsView.component.ts index fa80df730f..d50b295cce 100644 --- a/src/sql/workbench/contrib/jobManagement/browser/jobsView.component.ts +++ b/src/sql/workbench/contrib/jobManagement/browser/jobsView.component.ts @@ -21,7 +21,7 @@ import { IJobManagementService } from 'sql/workbench/services/jobManagement/comm import { JobManagementView, JobActionContext } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView'; import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -107,7 +107,8 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe @Inject(IContextMenuService) contextMenuService: IContextMenuService, @Inject(IKeybindingService) keybindingService: IKeybindingService, @Inject(IDashboardService) _dashboardService: IDashboardService, - @Inject(IAdsTelemetryService) private _telemetryService: IAdsTelemetryService + @Inject(IAdsTelemetryService) private _telemetryService: IAdsTelemetryService, + @Inject(IContextViewService) private _contextViewService: IContextViewService ) { super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent); let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap; @@ -181,7 +182,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe }); this.rowDetail = rowDetail; columns.unshift(this.rowDetail.getColumnDefinition()); - let filterPlugin = new HeaderFilter(); + let filterPlugin = new HeaderFilter(this._contextViewService); this._register(attachButtonStyler(filterPlugin, this._themeService)); this.filterPlugin = filterPlugin; jQuery(this._gridEl.nativeElement).empty(); diff --git a/src/sql/workbench/contrib/jobManagement/browser/notebooksView.component.ts b/src/sql/workbench/contrib/jobManagement/browser/notebooksView.component.ts index 70c93631c9..5e3b7daa3b 100644 --- a/src/sql/workbench/contrib/jobManagement/browser/notebooksView.component.ts +++ b/src/sql/workbench/contrib/jobManagement/browser/notebooksView.component.ts @@ -21,7 +21,7 @@ import { IJobManagementService } from 'sql/workbench/services/jobManagement/comm import { JobManagementView, JobActionContext } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView'; import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAction } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -106,7 +106,9 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit, @Inject(IContextMenuService) contextMenuService: IContextMenuService, @Inject(IKeybindingService) keybindingService: IKeybindingService, @Inject(IDashboardService) _dashboardService: IDashboardService, - @Inject(IAdsTelemetryService) private _telemetryService: IAdsTelemetryService + @Inject(IAdsTelemetryService) private _telemetryService: IAdsTelemetryService, + @Inject(IContextViewService) private _contextViewService: IContextViewService, + ) { super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent); let notebookCacheObjectMap = this._jobManagementService.notebookCacheObjectMap; @@ -180,7 +182,7 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit, }); this.rowDetail = rowDetail; columns.unshift(this.rowDetail.getColumnDefinition()); - let filterPlugin = new HeaderFilter(); + let filterPlugin = new HeaderFilter(this._contextViewService); this._register(attachButtonStyler(filterPlugin, this._themeService)); this.filterPlugin = filterPlugin; jQuery(this._gridEl.nativeElement).empty(); diff --git a/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts b/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts index 3326c204b0..21049aaac2 100644 --- a/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/outputs/gridOutput.component.ts @@ -7,7 +7,7 @@ import { OnInit, Component, Input, Inject, ViewChild, ElementRef } from '@angula import * as azdata from 'azdata'; import { IGridDataProvider, getResultsString } from 'sql/workbench/services/query/common/gridDataProvider'; -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 { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -207,12 +207,13 @@ class DataResourceTable extends GridTableBase { @IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService, @IConfigurationService configurationService: IConfigurationService, @IQueryModelService queryModelService: IQueryModelService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IContextViewService contextViewService: IContextViewService ) { super(state, createResultSet(source), { actionOrientation: ActionsOrientation.HORIZONTAL, inMemoryDataProcessing: true - }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService, queryModelService, themeService); + }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService, queryModelService, themeService, contextViewService); this._gridDataProvider = this.instantiationService.createInstance(DataResourceDataProvider, source, this.resultSet, this.cellModel); this._chart = this.instantiationService.createInstance(ChartView, false); diff --git a/src/sql/workbench/contrib/query/browser/gridPanel.ts b/src/sql/workbench/contrib/query/browser/gridPanel.ts index cf71179c89..1e60abf89e 100644 --- a/src/sql/workbench/contrib/query/browser/gridPanel.ts +++ b/src/sql/workbench/contrib/query/browser/gridPanel.ts @@ -22,7 +22,7 @@ import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additio import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin'; import { GridTable as HighPerfGridTable } from 'sql/workbench/contrib/query/browser/highPerfGridPanel'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { Emitter, Event } from 'vs/base/common/event'; @@ -385,7 +385,8 @@ export abstract class GridTableBase extends Disposable implements IView { @IUntitledTextEditorService private readonly untitledEditorService: IUntitledTextEditorService, @IConfigurationService protected readonly configurationService: IConfigurationService, @IQueryModelService private readonly queryModelService: IQueryModelService, - @IThemeService private readonly themeService: IThemeService + @IThemeService private readonly themeService: IThemeService, + @IContextViewService private readonly contextViewService: IContextViewService ) { super(); let config = this.configurationService.getValue<{ rowHeight: number }>('resultsGrid'); @@ -527,7 +528,7 @@ export abstract class GridTableBase extends Disposable implements IView { this.table.rerenderGrid(); })); if (this.enableFilteringFeature) { - this.filterPlugin = new HeaderFilter(); + this.filterPlugin = new HeaderFilter(this.contextViewService); attachButtonStyler(this.filterPlugin, this.themeService); this.table.registerPlugin(this.filterPlugin); } @@ -861,13 +862,14 @@ class GridTable extends GridTableBase { @IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService, @IConfigurationService configurationService: IConfigurationService, @IQueryModelService queryModelService: IQueryModelService, - @IThemeService themeService: IThemeService + @IThemeService themeService: IThemeService, + @IContextViewService contextViewService: IContextViewService ) { super(state, resultSet, { actionOrientation: ActionsOrientation.VERTICAL, inMemoryDataProcessing: true, inMemoryDataCountThreshold: configurationService.getValue('queryEditor').results.inMemoryDataProcessingThreshold, - }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService, queryModelService, themeService); + }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService, queryModelService, themeService, contextViewService); this._gridDataProvider = this.instantiationService.createInstance(QueryGridDataProvider, this._runner, resultSet.batchId, resultSet.id); } diff --git a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts index 60e5b0327f..bdb5fb4b7c 100644 --- a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts +++ b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts @@ -25,6 +25,7 @@ import { Emitter } from 'vs/base/common/event'; import { ContextMenuAnchor } from 'sql/workbench/contrib/resourceViewer/browser/resourceViewerEditor'; import { LoadingSpinnerPlugin } from 'sql/base/browser/ui/table/plugins/loadingSpinner.plugin'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; export class ResourceViewerTable extends Disposable { @@ -38,7 +39,8 @@ export class ResourceViewerTable extends Disposable { @IWorkbenchThemeService private _themeService: IWorkbenchThemeService, @IOpenerService private _openerService: IOpenerService, @ICommandService private _commandService: ICommandService, - @INotificationService private _notificationService: INotificationService) { + @INotificationService private _notificationService: INotificationService, + @IContextViewService private _contextViewService: IContextViewService) { super(); let filterFn = (data: Array): Array => { return data.filter(item => this.filter(item)); @@ -54,7 +56,7 @@ export class ResourceViewerTable extends Disposable { })); this._resourceViewerTable.setSelectionModel(new RowSelectionModel()); - let filterPlugin = new HeaderFilter(); + let filterPlugin = new HeaderFilter(this._contextViewService); this._register(attachButtonStyler(filterPlugin, this._themeService)); this._register(attachTableStyler(this._resourceViewerTable, this._themeService)); this._register(this._resourceViewerTable.onClick(this.onTableClick, this)); diff --git a/src/sql/workbench/test/electron-browser/modalComponents/table.component.test.ts b/src/sql/workbench/test/electron-browser/modalComponents/table.component.test.ts index 2f357f6e19..115a190777 100644 --- a/src/sql/workbench/test/electron-browser/modalComponents/table.component.test.ts +++ b/src/sql/workbench/test/electron-browser/modalComponents/table.component.test.ts @@ -19,7 +19,7 @@ suite('TableComponent Tests', () => { ['4', '5', '6'] ]; let columns = ['c1', 'c2', 'c3']; - const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService()); + const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService(), undefined); let actual = tableComponent.transformData(data, columns); let expected: { [key: string]: string }[] = [ @@ -39,7 +39,7 @@ suite('TableComponent Tests', () => { test('Table transformData should return empty array given undefined rows', () => { let data = undefined; - const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService()); + const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService(), undefined); let columns = ['c1', 'c2', 'c3']; let actual = tableComponent.transformData(data, columns); let expected: { [key: string]: string }[] = []; @@ -52,7 +52,7 @@ suite('TableComponent Tests', () => { ['4', '5', '6'] ]; let columns; - const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService()); + const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService(), undefined); let actual = tableComponent.transformData(data, columns); let expected: { [key: string]: string }[] = []; assert.deepEqual(actual, expected); @@ -63,7 +63,7 @@ suite('TableComponent Tests', () => { ['1', '2'], ['4', '5'] ]; - const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService()); + const tableComponent = new TableComponent(undefined, undefined, undefined, new NullLogService(), undefined); let columns = ['c1', 'c2', 'c3']; let actual = tableComponent.transformData(data, columns); let expected: { [key: string]: string }[] = [