diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts
index 672fb262a9..0685d42265 100644
--- a/src/sql/azdata.proposed.d.ts
+++ b/src/sql/azdata.proposed.d.ts
@@ -106,22 +106,55 @@ declare module 'azdata' {
DataGridProvider = 'DataGridProvider'
}
+ export type DataGridColumnType = 'hyperlink' | 'text' | 'image';
/**
* A column in a data grid
*/
export interface DataGridColumn {
/**
* The text to display on the column heading.
- **/
+ */
name: string;
+
/**
* The property name in the DataGridItem
- **/
+ */
field: string;
+
/**
* A unique identifier for the column within the grid.
*/
id: string;
+
+ /**
+ * The type of column this is. This is used to determine how to render the contents.
+ */
+ type: DataGridColumnType;
+
+ /**
+ * Whether this column is sortable.
+ */
+ sortable?: boolean;
+
+ /**
+ * Whether this column is filterable
+ */
+ filterable?: boolean;
+
+ /**
+ * If false, column can no longer be resized.
+ */
+ resizable?: boolean;
+
+ /**
+ * If set to a non-empty string, a tooltip will appear on hover containing the string.
+ */
+ tooltip?: string;
+
+ /**
+ * Width of the column in pixels.
+ */
+ width?: number
}
/**
@@ -132,10 +165,14 @@ declare module 'azdata' {
* A unique identifier for this item
*/
id: string;
+ /**
+ * The optional icon to display for this item
+ */
+ iconPath?: string;
/**
* The other properties that will be displayed in the grid
*/
- [key: string]: string;
+ [key: string]: any;
}
/**
diff --git a/src/sql/base/browser/ui/table/formatters.ts b/src/sql/base/browser/ui/table/formatters.ts
index 9bbf83c9be..7015d29bf6 100644
--- a/src/sql/base/browser/ui/table/formatters.ts
+++ b/src/sql/base/browser/ui/table/formatters.ts
@@ -68,6 +68,10 @@ export function textFormatter(row: number | undefined, cell: any | undefined, va
return `${valueToDisplay}`;
}
+export function imageFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string {
+ return `
`;
+}
+
/**
* Provide slick grid cell with encoded ariaLabel and plain text.
* text will be escaped by the textFormatter and ariaLabel will be consumed by slickgrid directly.
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 aa4e24fb01..ff0f58fda0 100644
--- a/src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts
+++ b/src/sql/base/browser/ui/table/plugins/headerFilter.plugin.ts
@@ -10,7 +10,7 @@ import { addDisposableListener } from 'vs/base/browser/dom';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { withNullAsUndefined } from 'vs/base/common/types';
-interface IExtendedColumn extends Slick.Column {
+export interface IExtendedColumn extends Slick.Column {
filterValues?: Array;
}
@@ -84,6 +84,9 @@ export class HeaderFilter {
if (column.id === '_detail_selector') {
return;
}
+ if ((column).filterable === false) {
+ return;
+ }
const $el = jQuery('')
.addClass('slick-header-menubutton')
.data('column', column);
diff --git a/src/sql/workbench/browser/editor/resourceViewer/resourceViewerInput.ts b/src/sql/workbench/browser/editor/resourceViewer/resourceViewerInput.ts
index be41c5fd90..acd1b117ca 100644
--- a/src/sql/workbench/browser/editor/resourceViewer/resourceViewerInput.ts
+++ b/src/sql/workbench/browser/editor/resourceViewer/resourceViewerInput.ts
@@ -7,38 +7,32 @@ import * as nls from 'vs/nls';
import { EditorInput } from 'vs/workbench/common/editor';
import { Event, Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
-import { ResourceViewerState } from 'sql/workbench/common/editor/resourceViewer/resourceViewerState';
-import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
+import { IDataGridProviderService } from 'sql/workbench/services/dataGridProvider/common/dataGridProviderService';
+import { onUnexpectedError } from 'vs/base/common/errors';
+import { getDataGridFormatter } from 'sql/workbench/services/dataGridProvider/browser/dataGridProviderUtils';
export interface ColumnDefinition extends Slick.Column {
name: string;
+ filterable?: boolean;
}
export class ResourceViewerInput extends EditorInput {
public static ID: string = 'workbench.editorInput.resourceViewerInput';
- private _data: TableDataView;
- private _columns: string[] = [];
- private _state: ResourceViewerState;
+ private _data: Slick.SlickData[] = [];
+ private _columns: ColumnDefinition[] = [];
private _onColumnsChanged = new Emitter[]>();
public onColumnsChanged: Event[]> = this._onColumnsChanged.event;
- constructor() {
- super();
- this._state = new ResourceViewerState();
- let searchFn = (val: { [x: string]: string }, exp: string): Array => {
- let ret = new Array();
- for (let i = 0; i < this._columns.length; i++) {
- let colVal = val[this._columns[i]];
- if (colVal && colVal.toLocaleLowerCase().indexOf(exp.toLocaleLowerCase()) > -1) {
- ret.push(i);
- }
- }
- return ret;
- };
+ private _onDataChanged = new Emitter();
+ public onDataChanged: Event = this._onDataChanged.event;
+
+ constructor(private _providerId: string, @IDataGridProviderService private _dataGridProvider: IDataGridProviderService) {
+ super();
+ this.fetchColumns();
+ this.fetchItems();
- this._data = new TableDataView(undefined, searchFn, undefined, undefined);
}
public getTypeId(): string {
@@ -49,39 +43,49 @@ export class ResourceViewerInput extends EditorInput {
return nls.localize('resourceViewerInput.resourceViewer', "Resource Viewer");
}
- public get data(): TableDataView {
+ public get data(): Slick.SlickData[] {
return this._data;
}
- public get columnDefinitions(): ColumnDefinition[] {
- if (this._columns) {
- return this._columns.map(i => {
- return {
- id: i,
- field: i,
- name: i,
- sortable: true
- };
- });
- } else {
- return [];
- }
- }
-
- public set columns(columns: Array) {
+ public set columns(columns: ColumnDefinition[]) {
this._columns = columns;
- this._onColumnsChanged.fire(this.columnDefinitions);
+ this._onColumnsChanged.fire(this._columns);
}
- public get state(): ResourceViewerState {
- return this._state;
+ public get columns(): ColumnDefinition[] {
+ return this._columns;
}
isDirty(): boolean {
- return false; // TODO chgagnon implement
+ return false;
}
public get resource(): URI | undefined {
return undefined;
}
+
+ private fetchColumns(): void {
+ this._dataGridProvider.getDataGridColumns(this._providerId).then(columns => {
+ this.columns = columns.map(col => {
+ return {
+ name: col.name,
+ field: col.field,
+ id: col.id,
+ formatter: getDataGridFormatter(col.type),
+ sortable: col.sortable ?? true,
+ filterable: col.filterable ?? true,
+ resizable: col.resizable ?? true,
+ tooltip: col.tooltip,
+ width: col.width
+ };
+ });
+ }).catch(err => onUnexpectedError(err));
+ }
+
+ private fetchItems(): void {
+ this._dataGridProvider.getDataGridItems(this._providerId).then(items => {
+ this._data = items;
+ this._onDataChanged.fire();
+ }).catch(err => onUnexpectedError(err));
+ }
}
diff --git a/src/sql/workbench/common/editor/resourceViewer/resourceViewerState.ts b/src/sql/workbench/common/editor/resourceViewer/resourceViewerState.ts
deleted file mode 100644
index 8f2dba9e37..0000000000
--- a/src/sql/workbench/common/editor/resourceViewer/resourceViewerState.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the Source EULA. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import { IDisposable } from 'vs/base/common/lifecycle';
-import { Emitter } from 'vs/base/common/event';
-
-export interface IResourceViewerStateChangedEvent {
-
-}
-
-export interface INewResourceViewerState {
- // TODO - chgagnon implement state
-}
-
-export class ResourceViewerState implements IDisposable {
-
- private readonly _onResourceViewerStateChange = new Emitter();
- public readonly onResourceViewerStateChange = this._onResourceViewerStateChange.event;
-
- public dispose(): void {
- }
-
- public change(newState: INewResourceViewerState): void {
- let changeEvent: IResourceViewerStateChangedEvent = {
- };
- let somethingChanged = false;
-
- if (somethingChanged) {
- this._onResourceViewerStateChange.fire(changeEvent);
- }
- }
-}
diff --git a/src/sql/workbench/contrib/resourceViewer/browser/media/resourceViewerTable.css b/src/sql/workbench/contrib/resourceViewer/browser/media/resourceViewerTable.css
new file mode 100644
index 0000000000..7ac236d6a3
--- /dev/null
+++ b/src/sql/workbench/contrib/resourceViewer/browser/media/resourceViewerTable.css
@@ -0,0 +1,9 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+.resource-viewer-table .slick-cell {
+ border-right: none;
+}
+
diff --git a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewer.contribution.ts b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewer.contribution.ts
index f8ef9a858a..467e8cfb04 100644
--- a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewer.contribution.ts
+++ b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewer.contribution.ts
@@ -11,14 +11,18 @@ import { ResourceViewerInput } from 'sql/workbench/browser/editor/resourceViewer
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
+import { isString } from 'vs/base/common/types';
CommandsRegistry.registerCommand({
- id: 'resourceViewer.newResourceViewer',
- handler: (accessor: ServicesAccessor, ...args: any[]): void => {
+ id: 'resourceViewer.openResourceViewer',
+ handler: async (accessor: ServicesAccessor, ...args: any[]): Promise => {
const instantiationService: IInstantiationService = accessor.get(IInstantiationService);
const editorService: IEditorService = accessor.get(IEditorService);
+ if (!isString(args[0])) {
+ throw new Error('First argument must be the ProviderId');
+ }
- const resourceViewerInput = instantiationService.createInstance(ResourceViewerInput);
+ const resourceViewerInput = instantiationService.createInstance(ResourceViewerInput, args[0]);
editorService.openEditor(resourceViewerInput, { pinned: true }, ACTIVE_GROUP);
}
});
diff --git a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerEditor.ts b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerEditor.ts
index f727f67c83..00990e85c6 100644
--- a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerEditor.ts
+++ b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerEditor.ts
@@ -12,7 +12,6 @@ import { EditorOptions, IEditorOpenContext } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
-import { IResourceViewerStateChangedEvent } from 'sql/workbench/common/editor/resourceViewer/resourceViewerState';
import { ResourceViewerInput } from 'sql/workbench/browser/editor/resourceViewer/resourceViewerInput';
import { ResourceViewerTable } from 'sql/workbench/contrib/resourceViewer/browser/resourceViewerTable';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
@@ -39,22 +38,22 @@ export class ResourceViewerEditor extends EditorPane {
this._container.className = 'resource-viewer';
parent.appendChild(this._container);
- this._createHeader();
-
- let tableContainer = this.createResourceViewerTable();
+ const header = this.createHeader();
+ const tableContainer = this.createResourceViewerTable();
+ this._container.appendChild(header);
this._container.appendChild(tableContainer);
}
- private _createHeader(): void {
+ private createHeader(): HTMLElement {
const header = document.createElement('div');
header.className = 'resource-viewer-header';
- this._container.appendChild(header);
this._actionBar = this._register(new Taskbar(header));
this._actionBar.setContent([
// TODO - chgagnon add actions
]);
+ return header;
}
private createResourceViewerTable(): HTMLElement {
@@ -78,33 +77,19 @@ export class ResourceViewerEditor extends EditorPane {
this._inputDisposables.clear();
this._resourceViewerTable.data = input.data;
+ this._resourceViewerTable.columns = input.columns;
this._inputDisposables.add(input.onColumnsChanged(columns => {
this._resourceViewerTable.columns = columns;
}));
-
- this._inputDisposables.add(input.data.onRowCountChange(() => {
- this._resourceViewerTable.updateRowCount();
- }));
-
- this._inputDisposables.add(input.data.onFilterStateChange(() => {
- this._resourceViewerTable.invalidateAllRows();
- this._resourceViewerTable.updateRowCount();
+ this._inputDisposables.add(input.onDataChanged(() => {
+ this._resourceViewerTable.data = input.data;
}));
this._actionBar.context = input;
- this._inputDisposables.add(input.state.onResourceViewerStateChange(e => this.onStateChange(e)));
- this.onStateChange({
- });
-
this._resourceViewerTable.focus();
}
-
- private onStateChange(e: IResourceViewerStateChangedEvent): void {
-
- }
-
public layout(dimension: DOM.Dimension): void {
this._container.style.width = dimension.width + 'px';
this._container.style.height = dimension.height + 'px';
diff --git a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts
index d0e76e3f9a..f153d40ae8 100644
--- a/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts
+++ b/src/sql/workbench/contrib/resourceViewer/browser/resourceViewerTable.ts
@@ -3,65 +3,92 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
+import 'vs/css!./media/resourceViewerTable';
import { Table } from 'sql/base/browser/ui/table/table';
-import { attachTableStyler } from 'sql/platform/theme/common/styler';
+import { attachTableStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
-import { Dimension } from 'vs/base/browser/dom';
-import { textFormatter, slickGridDataItemColumnValueExtractor } from 'sql/base/browser/ui/table/formatters';
-import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
+import { slickGridDataItemColumnValueExtractor } from 'sql/base/browser/ui/table/formatters';
+import { HeaderFilter, CommandEventArgs, IExtendedColumn } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
import { Disposable } from 'vs/base/common/lifecycle';
+import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
export class ResourceViewerTable extends Disposable {
private _resourceViewerTable!: Table;
- private _data: TableDataView | undefined;
+ private _dataView: TableDataView;
constructor(parent: HTMLElement, @IWorkbenchThemeService private _themeService: IWorkbenchThemeService) {
super();
+ let filterFn = (data: Array): Array => {
+ return data.filter(item => this.filter(item));
+ };
+
+ this._dataView = new TableDataView(undefined, undefined, undefined, filterFn);
this._resourceViewerTable = this._register(new Table(parent, {
sorter: (args) => {
- this._data?.sort(args);
+ this._dataView.sort(args);
}
}, {
dataItemColumnValueExtractor: slickGridDataItemColumnValueExtractor
}));
this._resourceViewerTable.setSelectionModel(new RowSelectionModel());
- attachTableStyler(this._resourceViewerTable, this._themeService);
+ let filterPlugin = new HeaderFilter();
+ this._register(attachButtonStyler(filterPlugin, this._themeService));
+ this._register(attachTableStyler(this._resourceViewerTable, this._themeService));
+ filterPlugin.onFilterApplied.subscribe(() => {
+ this._dataView.filter();
+ this._resourceViewerTable.grid.render();
+ this._resourceViewerTable.grid.resetActiveCell();
+ this._resourceViewerTable.grid.resizeCanvas();
+ });
+ filterPlugin.onCommand.subscribe((e, args: CommandEventArgs) => {
+ // Convert filter command to SlickGrid sort args
+ this._dataView.sort({
+ grid: args.grid,
+ multiColumnSort: false,
+ sortCol: args.column,
+ sortAsc: args.command === 'sort-asc'
+ });
+ this._resourceViewerTable.grid.invalidate();
+ this._resourceViewerTable.grid.render();
+ });
+ this._resourceViewerTable.registerPlugin(filterPlugin);
}
- public set data(data: TableDataView) {
- this._data = data;
- this._resourceViewerTable.setData(data);
+ public set data(data: Slick.SlickData[]) {
+ this._dataView.clear();
+ this._dataView.push(data);
+ this._resourceViewerTable.grid.setData(this._dataView, true);
+ this._resourceViewerTable.grid.render();
}
public set columns(columns: Slick.Column[]) {
- this._resourceViewerTable.columns = columns.map(column => {
- column.formatter = textFormatter;
- return column;
- });
- this._resourceViewerTable.autosizeColumns();
- }
-
- public updateRowCount(): void {
- this._resourceViewerTable.updateRowCount();
- }
-
- public invalidateAllRows(): void {
- this._resourceViewerTable.grid.invalidateAllRows();
- }
-
- public autosizeColumns(): void {
- this._resourceViewerTable.autosizeColumns();
+ this._resourceViewerTable.columns = columns;
}
public focus(): void {
this._resourceViewerTable.focus();
}
- public layout(dimension: Dimension): void {
- this._resourceViewerTable.layout(dimension);
- this._resourceViewerTable.autosizeColumns();
+ private filter(item: Slick.SlickData) {
+ const columns = this._resourceViewerTable.grid.getColumns();
+ let value = true;
+ for (let i = 0; i < columns.length; i++) {
+ const col: IExtendedColumn = columns[i];
+ if (!col.field) {
+ continue;
+ }
+ let filterValues = col.filterValues;
+ if (filterValues && filterValues.length > 0) {
+ if (item._parent) {
+ value = value && !!filterValues.find(x => x === item._parent[col.field!]);
+ } else {
+ value = value && !!filterValues.find(x => x === item[col.field!]);
+ }
+ }
+ }
+ return value;
}
}
diff --git a/src/sql/workbench/services/dataGridProvider/browser/dataGridProviderUtils.ts b/src/sql/workbench/services/dataGridProvider/browser/dataGridProviderUtils.ts
new file mode 100644
index 0000000000..664bd9735c
--- /dev/null
+++ b/src/sql/workbench/services/dataGridProvider/browser/dataGridProviderUtils.ts
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as azdata from 'azdata';
+import { textFormatter, hyperLinkFormatter, imageFormatter } from 'sql/base/browser/ui/table/formatters';
+
+export function getDataGridFormatter(formatterType: azdata.DataGridColumnType): Slick.Formatter {
+ switch (formatterType) {
+ case 'text':
+ return textFormatter;
+ case 'hyperlink':
+ return hyperLinkFormatter;
+ case 'image':
+ return imageFormatter;
+ }
+}