diff --git a/package.json b/package.json index bae345e99f..6e058f2575 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "@angular/platform-browser-dynamic": "~4.1.3", "@angular/router": "~4.1.3", "angular2-grid": "2.0.6", - "angular2-slickgrid": "github:Microsoft/angular2-slickgrid#1.4.6", "ansi_up": "^3.0.0", "applicationinsights": "1.0.8", "chart.js": "^2.6.0", diff --git a/src/sql/base/browser/ui/table/asyncDataView.ts b/src/sql/base/browser/ui/table/asyncDataView.ts index f037e5b82e..f35bfe3d86 100644 --- a/src/sql/base/browser/ui/table/asyncDataView.ts +++ b/src/sql/base/browser/ui/table/asyncDataView.ts @@ -15,6 +15,10 @@ export interface IObservableCollection { dispose(): void; } +export interface ISlickColumn extends Slick.Column { + isEditable?: boolean; +} + class DataWindow { private _data: T[] | undefined; private _length: number = 0; @@ -179,7 +183,7 @@ export class VirtualizedCollection implements IObserv return this.placeHolderGenerator(index); } - private resetWindowsAroundIndex(index: number): void { + public resetWindowsAroundIndex(index: number): void { let bufferWindowBeforeStart = Math.max(0, index - this.windowSize * 1.5); let bufferWindowBeforeEnd = Math.max(0, index - this.windowSize / 2); diff --git a/src/sql/base/browser/ui/table/media/slickColorTheme.css b/src/sql/base/browser/ui/table/media/slickColorTheme.css index c2c6285e77..9054280b72 100644 --- a/src/sql/base/browser/ui/table/media/slickColorTheme.css +++ b/src/sql/base/browser/ui/table/media/slickColorTheme.css @@ -69,8 +69,8 @@ --color-grid-dirty-text: #101010; } /* grid styling */ - -.vs slick-grid.active .grid .slick-cell.active { +.vs .slickgridContainer .grid .slick-cell .active, +.vs slick-grid.active .grid .slick-cell .active { border-color: var(--color-cell-border-active); } @@ -204,7 +204,7 @@ } /* grid styling */ - +.vs-dark .slickgridContainer .grid .slick-cell.active, .vs-dark slick-grid.active .grid .slick-cell.active { border-color: var(--color-cell-border-active); } diff --git a/src/sql/base/browser/ui/table/table.ts b/src/sql/base/browser/ui/table/table.ts index 2438736d92..4464c87673 100644 --- a/src/sql/base/browser/ui/table/table.ts +++ b/src/sql/base/browser/ui/table/table.ts @@ -19,6 +19,7 @@ import { Widget } from 'vs/base/browser/ui/widget'; import { isArray, isBoolean } from 'vs/base/common/types'; import { Event, Emitter } from 'vs/base/common/event'; import { range } from 'vs/base/common/arrays'; +import { AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView'; function getDefaultOptions(): Slick.GridOptions { return >{ @@ -49,6 +50,9 @@ export class Table extends Widget implements IDisposa private _onClick = new Emitter(); public readonly onClick: Event = this._onClick.event; + private _onHeaderClick = new Emitter(); + public readonly onHeaderClick: Event = this._onHeaderClick.event; + private _onColumnResize = new Emitter(); public readonly onColumnResize = this._onColumnResize.event; @@ -111,9 +115,17 @@ export class Table extends Widget implements IDisposa this.mapMouseEvent(this._grid.onContextMenu, this._onContextMenu); this.mapMouseEvent(this._grid.onClick, this._onClick); + this.mapMouseEvent(this._grid.onHeaderClick, this._onHeaderClick); this._grid.onColumnsResized.subscribe(() => this._onColumnResize.fire()); } + public rerenderGrid(start: number, end: number) { + this._grid.updateRowCount(); + this._grid.setColumns(this._grid.getColumns()); + this._grid.invalidateAllRows(); + this._grid.render(); + } + private mapMouseEvent(slickEvent: Slick.Event, emitter: Emitter) { slickEvent.subscribe((e: JQuery.Event) => { const originalEvent = e.originalEvent; @@ -151,8 +163,9 @@ export class Table extends Widget implements IDisposa setData(data: Array): void; setData(data: TableDataView): void; - setData(data: Array | TableDataView): void { - if (data instanceof TableDataView) { + setData(data: AsyncDataProvider): void; + setData(data: Array | TableDataView | AsyncDataProvider): void { + if (data instanceof TableDataView || data instanceof AsyncDataProvider) { this._data = data; } else { this._data = new TableDataView(data); @@ -197,6 +210,18 @@ export class Table extends Widget implements IDisposa this._grid.setSelectionModel(model); } + getSelectionModel(): Slick.SelectionModel> { + return this._grid.getSelectionModel(); + } + + getSelectedRanges(): Slick.Range[] { + let selectionModel = this._grid.getSelectionModel(); + if (selectionModel && selectionModel.getSelectedRanges) { + return selectionModel.getSelectedRanges(); + } + return undefined; + } + focus(): void { this._grid.focus(); } @@ -205,6 +230,10 @@ export class Table extends Widget implements IDisposa this._grid.setActiveCell(row, cell); } + setActive(): void { + this._grid.setActiveCell(0, 1); + } + get activeCell(): Slick.Cell { return this._grid.getActiveCell(); } diff --git a/src/sql/platform/query/common/queryModel.ts b/src/sql/platform/query/common/queryModel.ts index 94f9e39d2d..58440cfb3f 100644 --- a/src/sql/platform/query/common/queryModel.ts +++ b/src/sql/platform/query/common/queryModel.ts @@ -63,7 +63,7 @@ export interface IQueryModelService { refreshResultsets(uri: string): void; sendGridContentEvent(uri: string, eventName: string): void; resizeResultsets(uri: string): void; - onAngularLoaded(uri: string): void; + onLoaded(uri: string): void; copyResults(uri: string, selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void; showCommitError(error: string): void; diff --git a/src/sql/platform/query/common/queryModelService.ts b/src/sql/platform/query/common/queryModelService.ts index 57c949fe50..e28fe88b38 100644 --- a/src/sql/platform/query/common/queryModelService.ts +++ b/src/sql/platform/query/common/queryModelService.ts @@ -116,12 +116,11 @@ export class QueryModelService implements IQueryModelService { } /** - * To be called by an angular component's DataService when the component has finished loading. + * To be called by a component's DataService when the component has finished loading. * Sends all previously enqueued query events to the DataService and signals to stop enqueuing - * any further events. This prevents QueryEvents from getting lost if they are sent before - * angular is listening for them. + * any further events. */ - public onAngularLoaded(uri: string) { + public onLoaded(uri: string) { if (this._queryInfoMap.has(uri)) { let info = this._getQueryInfo(uri)!; info.dataServiceReady = true; diff --git a/src/sql/platform/query/test/common/testQueryModelService.ts b/src/sql/platform/query/test/common/testQueryModelService.ts index 5bddd69f9e..ee9cda3122 100644 --- a/src/sql/platform/query/test/common/testQueryModelService.ts +++ b/src/sql/platform/query/test/common/testQueryModelService.ts @@ -56,7 +56,7 @@ export class TestQueryModelService implements IQueryModelService { resizeResultsets(uri: string): void { throw new Error('Method not implemented.'); } - onAngularLoaded(uri: string): void { + onLoaded(uri: string): void { throw new Error('Method not implemented.'); } copyResults(uri: string, selection: Slick.Range[], batchId: number, resultId: number, includeHeaders?: boolean): void { diff --git a/src/sql/workbench/contrib/editData/browser/editData.component.html b/src/sql/workbench/contrib/editData/browser/editData.component.html deleted file mode 100644 index 425f6e1f8b..0000000000 --- a/src/sql/workbench/contrib/editData/browser/editData.component.html +++ /dev/null @@ -1,33 +0,0 @@ - - -
-
-
- - -
-
-
\ No newline at end of file diff --git a/src/sql/workbench/contrib/editData/browser/editData.module.ts b/src/sql/workbench/contrib/editData/browser/editData.module.ts deleted file mode 100644 index de8f5a5e28..0000000000 --- a/src/sql/workbench/contrib/editData/browser/editData.module.ts +++ /dev/null @@ -1,57 +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 { ApplicationRef, ComponentFactoryResolver, NgModule, Inject, forwardRef, Type } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { BrowserModule } from '@angular/platform-browser'; -import { SlickGrid } from 'angular2-slickgrid'; - -import { EditDataComponent } from 'sql/workbench/contrib/editData/browser/editData.component'; -import { providerIterator } from 'sql/workbench/services/bootstrap/browser/bootstrapService'; - -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IBootstrapParams, ISelector } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; - -export const EditDataModule = (params: IBootstrapParams, selector: string, instantiationService: IInstantiationService): Type => { - - @NgModule({ - - imports: [ - CommonModule, - BrowserModule - ], - - declarations: [ - EditDataComponent, - SlickGrid - ], - - entryComponents: [ - EditDataComponent - ], - providers: [ - { provide: IBootstrapParams, useValue: params }, - { provide: ISelector, useValue: selector }, - ...providerIterator(instantiationService) - ] - }) - class ModuleClass { - - constructor( - @Inject(forwardRef(() => ComponentFactoryResolver)) private _resolver: ComponentFactoryResolver, - @Inject(ISelector) private selector: string - ) { - } - - ngDoBootstrap(appRef: ApplicationRef) { - const factory = this._resolver.resolveComponentFactory(EditDataComponent); - (factory).factory.selector = this.selector; - appRef.bootstrap(factory); - } - } - - return ModuleClass; -}; diff --git a/src/sql/workbench/contrib/editData/browser/editData.component.ts b/src/sql/workbench/contrib/editData/browser/editDataGridPanel.ts similarity index 62% rename from src/sql/workbench/contrib/editData/browser/editData.component.ts rename to src/sql/workbench/contrib/editData/browser/editDataGridPanel.ts index c47fd8d48a..c673a27d52 100644 --- a/src/sql/workbench/contrib/editData/browser/editData.component.ts +++ b/src/sql/workbench/contrib/editData/browser/editDataGridPanel.ts @@ -5,12 +5,11 @@ import 'vs/css!./media/editData'; -import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core'; -import { VirtualizedCollection } from 'angular2-slickgrid'; +import { VirtualizedCollection, AsyncDataProvider, ISlickColumn } from 'sql/base/browser/ui/table/asyncDataView'; +import { Table } from 'sql/base/browser/ui/table/table'; import { IGridDataSet } from 'sql/workbench/contrib/grid/common/interfaces'; import * as Services from 'sql/base/browser/ui/table/formatters'; -import { IEditDataComponentParams, IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; import { GridParentComponent } from 'sql/workbench/contrib/editData/browser/gridParentComponent'; import { EditDataGridActionProvider } from 'sql/workbench/contrib/editData/browser/editDataGridActions'; import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService'; @@ -18,7 +17,7 @@ import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColu import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin'; import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin'; import { escape } from 'sql/base/common/strings'; - +import { DataService } from 'sql/workbench/contrib/grid/common/dataService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import Severity from 'vs/base/common/severity'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -32,14 +31,10 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EditUpdateCellResult } from 'azdata'; import { ILogService } from 'vs/platform/log/common/log'; import { deepClone, assign } from 'vs/base/common/objects'; -export const EDITDATA_SELECTOR: string = 'editdata-component'; +import { Emitter, Event } from 'vs/base/common/event'; +import { equals } from 'vs/base/common/arrays'; -@Component({ - selector: EDITDATA_SELECTOR, - host: { '(window:keydown)': 'keyEvent($event)', '(window:gridnav)': 'keyEvent($event)' }, - templateUrl: decodeURI(require.toUrl('./editData.component.html')) -}) -export class EditDataComponent extends GridParentComponent implements OnInit, OnDestroy { +export class EditDataGridPanel extends GridParentComponent { // The time(in milliseconds) we wait before refreshing the grid. // We use clearTimeout and setTimeout pair to avoid unnecessary refreshes. private refreshGridTimeoutInMs = 200; @@ -53,8 +48,12 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On // FIELDS // All datasets + private gridDataProvider: AsyncDataProvider; private dataSet: IGridDataSet; + private oldDataRows: VirtualizedCollection; private firstRender = true; + private firstLoad = true; + private enableEditing = true; // Current selected cell state private currentCell: { row: number, column: number, isEditable: boolean, isDirty: boolean }; private currentEditCellValue: string; @@ -62,8 +61,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On private removingNewRow: boolean; private rowIdMappings: { [gridRowId: number]: number } = {}; private dirtyCells: number[] = []; - protected plugins = new Array>>(); - + protected plugins = new Array>(); + // List of column names with their indexes stored. + private columnNameToIndex: { [columnNumber: number]: string } = {}; // Edit Data functions public onActiveCellChanged: (event: Slick.OnActiveCellChangedEventArgs) => void; public onCellEditEnd: (event: Slick.OnCellChangeEventArgs) => void; @@ -81,31 +81,33 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On }; constructor( - @Inject(forwardRef(() => ElementRef)) el: ElementRef, - @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef, - @Inject(IBootstrapParams) params: IEditDataComponentParams, - @Inject(IInstantiationService) private instantiationService: IInstantiationService, - @Inject(INotificationService) private notificationService: INotificationService, - @Inject(IContextMenuService) contextMenuService: IContextMenuService, - @Inject(IKeybindingService) keybindingService: IKeybindingService, - @Inject(IContextKeyService) contextKeyService: IContextKeyService, - @Inject(IConfigurationService) configurationService: IConfigurationService, - @Inject(IClipboardService) clipboardService: IClipboardService, - @Inject(IQueryEditorService) queryEditorService: IQueryEditorService, - @Inject(ILogService) logService: ILogService + dataService: DataService, + onSaveViewState: Event, + onRestoreViewState: Event, + @IInstantiationService protected instantiationService: IInstantiationService, + @INotificationService protected notificationService: INotificationService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IKeybindingService protected keybindingService: IKeybindingService, + @IContextKeyService protected contextKeyService: IContextKeyService, + @IConfigurationService protected configurationService: IConfigurationService, + @IClipboardService protected clipboardService: IClipboardService, + @IQueryEditorService protected queryEditorService: IQueryEditorService, + @ILogService protected logService: ILogService ) { - super(el, cd, contextMenuService, keybindingService, contextKeyService, configurationService, clipboardService, queryEditorService, logService); - this._el.nativeElement.className = 'slickgridContainer'; - this.dataService = params.dataService; + super(contextMenuService, keybindingService, contextKeyService, configurationService, clipboardService, queryEditorService, logService); + this.nativeElement = document.createElement('editdatagridpanel'); + this.nativeElement.className = 'slickgridContainer'; + this.dataService = dataService; this.actionProvider = this.instantiationService.createInstance(EditDataGridActionProvider, this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow()); - params.onRestoreViewState(() => this.restoreViewState()); - params.onSaveViewState(() => this.saveViewState()); + onRestoreViewState(() => this.restoreViewState()); + onSaveViewState(() => this.saveViewState()); + this.onInit(); } /** - * Called by Angular when the object is initialized + * Called when the object is initialized */ - ngOnInit(): void { + onInit(): void { const self = this; this.baseInit(); @@ -132,27 +134,29 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On this.logService.error('Unexpected query event type "' + event.type + '" sent'); break; } - self._cd.detectChanges(); }); + this.dataService.onLoaded(); + } - this.dataService.onAngularLoaded(); + public render(container: HTMLElement): void { + container.appendChild(this.nativeElement); } protected initShortcuts(shortcuts: { [name: string]: Function }): void { // TODO add any Edit Data-specific shortcuts here } - public ngOnDestroy(): void { + public onDestroy(): void { this.baseDestroy(); } - handleStart(self: EditDataComponent, event: any): void { + handleStart(self: EditDataGridPanel, event: any): void { self.dataSet = undefined; + self.oldDataRows = undefined; self.placeHolderDataSets = []; self.renderedDataSets = self.placeHolderDataSets; - this._cd.detectChanges(); - // Hooking up edit functions + // Hooking up edit functions handle this.onIsCellEditValid = (row, column, value): boolean => { // TODO can only run sync code return true; @@ -322,14 +326,14 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On cellSelectTasks.catch(() => { }); } - handleComplete(self: EditDataComponent, event: any): void { + handleComplete(self: EditDataGridPanel, event: any): void { } handleEditSessionReady(self, event): void { // TODO: update when edit session is ready } - handleMessage(self: EditDataComponent, event: any): void { + handleMessage(self: EditDataGridPanel, event: any): void { if (event.data && event.data.isError) { self.notificationService.notify({ severity: Severity.Error, @@ -338,7 +342,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On } } - handleResultSet(self: EditDataComponent, event: any): void { + handleResultSet(self: EditDataGridPanel, event: any): void { // Clone the data before altering it to avoid impacting other subscribers let resultSet = assign({}, event.data); if (!resultSet.complete) { @@ -363,9 +367,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On minHeight: minHeight, dataRows: new VirtualizedCollection( self.windowSize, + index => { return {}; }, resultSet.rowCount, this.loadDataFunction, - index => { return {}; } ), columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => { let columnIndex = (i + 1).toString(); @@ -378,14 +382,15 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On }; })) }; - self.plugins.push([rowNumberColumn, new AutoColumnSize({ maxWidth: this.configurationService.getValue('resultsGrid.maxColumnWidth') }), new AdditionalKeyBindings()]); + self.plugins.push(rowNumberColumn, new AutoColumnSize({ maxWidth: this.configurationService.getValue('resultsGrid.maxColumnWidth') }), new AdditionalKeyBindings()); self.dataSet = dataSet; + self.gridDataProvider = new AsyncDataProvider(dataSet.dataRows); // Create a dataSet to render without rows to reduce DOM size let undefinedDataSet = deepClone(dataSet); undefinedDataSet.columnDefinitions = dataSet.columnDefinitions; undefinedDataSet.dataRows = undefined; - undefinedDataSet.resized = new EventEmitter(); + undefinedDataSet.resized = new Emitter(); self.placeHolderDataSets.push(undefinedDataSet); self.refreshGrid(); @@ -395,6 +400,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On this.removingNewRow = false; this.newRowVisible = false; this.dirtyCells = []; + } /** @@ -414,34 +420,54 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On } private refreshGrid(): Thenable { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { const self = this; clearTimeout(self.refreshGridTimeoutHandle); this.refreshGridTimeoutHandle = setTimeout(() => { - for (let i = 0; i < self.placeHolderDataSets.length; i++) { - self.placeHolderDataSets[i].dataRows = self.dataSet.dataRows; - self.placeHolderDataSets[i].resized.emit(); + + if (self.dataSet && self.placeHolderDataSets[0].resized) { + self.placeHolderDataSets[0].dataRows = self.dataSet.dataRows; + self.placeHolderDataSets[0].resized.fire(); } - self._cd.detectChanges(); + + if (self.oldDataRows !== self.placeHolderDataSets[0].dataRows) { + self.detectChange(); + self.oldDataRows = self.placeHolderDataSets[0].dataRows; + } if (self.firstRender) { let setActive = function () { - if (self.firstRender && self.slickgrids.toArray().length > 0) { - self.slickgrids.toArray()[0].setActive(); + if (self.firstRender && self.table) { + self.table.setActive(); self.firstRender = false; } }; - - setTimeout(() => { - setActive(); - }); + setTimeout(() => setActive()); } resolve(); }, self.refreshGridTimeoutInMs); }); } + protected detectChange(): void { + if (this.firstLoad) { + this.handleChanges({ + ['dataRows']: { currentValue: this.dataSet.dataRows, firstChange: this.firstLoad, previousValue: undefined }, + ['columnDefinitions']: { currentValue: this.dataSet.columnDefinitions, firstChange: this.firstLoad, previousValue: undefined } + }); + this.handleInitializeTable(); + this.firstLoad = false; + } + else { + + this.table.setData(this.gridDataProvider); + this.handleChanges({ + ['dataRows']: { currentValue: this.dataSet.dataRows, firstChange: this.firstLoad, previousValue: this.oldDataRows } + }); + } + } + protected tryHandleKeyEvent(e: StandardKeyboardEvent): boolean { let handled: boolean = false; @@ -452,6 +478,24 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On return handled; } + /** + * Force re-rendering of the results grids. Calling this upon unhide (upon focus) fixes UI + * glitches that occur when a QueryResultsEditor is hidden then unhidden while it is running a query. + */ + refreshDatasets(): void { + let tempRenderedDataSets = this.renderedDataSets; + this.renderedDataSets = []; + this.handleChanges({ + ['dataRows']: { currentValue: undefined, firstChange: this.firstLoad, previousValue: this.dataSet.dataRows }, + ['columnDefinitions']: { currentValue: undefined, firstChange: this.firstLoad, previousValue: this.dataSet.columnDefinitions } + }); + this.renderedDataSets = tempRenderedDataSets; + this.handleChanges({ + ['dataRows']: { currentValue: this.renderedDataSets[0].dataRows, firstChange: this.firstLoad, previousValue: undefined }, + ['columnDefinitions']: { currentValue: this.renderedDataSets[0].columnDefinitions, firstChange: this.firstLoad, previousValue: undefined } + }); + } + // Private Helper Functions //////////////////////////////////////////////////////////////////////////// private async revertCurrentRow(): Promise { @@ -537,7 +581,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On // Adds CSS classes to slickgrid cells to indicate a dirty state private setCellDirtyState(row: number, column: number, dirtyState: boolean): void { - let slick: any = this.slickgrids.toArray()[0]; + let slick: any = this.table; let grid = slick._grid; if (dirtyState) { // Change cell color @@ -555,7 +599,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On // Adds CSS classes to slickgrid rows to indicate a dirty state private setRowDirtyState(row: number, dirtyState: boolean): void { - let slick: any = this.slickgrids.toArray()[0]; + let slick: any = this.table; let grid = slick._grid; if (dirtyState) { // Change row header color @@ -593,10 +637,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On self.dataSet.minHeight = self.getMinHeight(self.dataSet.totalRows); self.dataSet.dataRows = new VirtualizedCollection( self.windowSize, + index => { return {}; }, self.dataSet.totalRows, self.loadDataFunction, - index => { return {}; } ); + self.gridDataProvider = new AsyncDataProvider(self.dataSet.dataRows); }); } @@ -608,11 +653,11 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On this.dataSet.totalRows--; this.dataSet.dataRows = new VirtualizedCollection( this.windowSize, + index => { return {}; }, this.dataSet.totalRows, this.loadDataFunction, - index => { return {}; } ); - + this.gridDataProvider = new AsyncDataProvider(this.dataSet.dataRows); // refresh results view return this.refreshGrid().then(() => { // Set focus to the row index column of the removed row if the current selection is in the removed row @@ -624,25 +669,25 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On } private focusCell(row: number, column: number, forceEdit: boolean = true): void { - let slick: any = this.slickgrids.toArray()[0]; + let slick: any = this.table; let grid = slick._grid; grid.gotoCell(row, column, forceEdit); } private getMaxHeight(rowCount: number): any { - return rowCount < this._defaultNumShowingRows - ? ((rowCount + 1) * this._rowHeight) + 10 + return rowCount < this.defaultNumShowingRows + ? ((rowCount + 1) * this.rowHeight) + 10 : 'inherit'; } private getMinHeight(rowCount: number): any { - return rowCount > this._defaultNumShowingRows - ? (this._defaultNumShowingRows + 1) * this._rowHeight + 10 + return rowCount > this.defaultNumShowingRows + ? (this.defaultNumShowingRows + 1) * this.rowHeight + 10 : this.getMaxHeight(rowCount); } private saveViewState(): void { - let grid = this.slickgrids.toArray()[0]; + let grid = this.table; let self = this; if (grid) { let gridSelections = grid.getSelectedRanges(); @@ -671,8 +716,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On private restoreViewState(): void { if (this.savedViewState) { - this.slickgrids.toArray()[0].selection = this.savedViewState.gridSelections; - let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement; + // Row selections are undefined in original slickgrid, removed for no purpose + let viewport = ((this.table as any)._grid.getCanvasNode() as HTMLElement).parentElement; viewport.scrollLeft = this.savedViewState.scrollLeft; viewport.scrollTop = this.savedViewState.scrollTop; this.savedViewState = undefined; @@ -700,7 +745,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On } private isCellOnScreen(row: number, column: number): boolean { - let slick: any = this.slickgrids.toArray()[0]; + let slick: any = this.table; let grid = slick._grid; let viewport = grid.getViewport(); let cellBox = grid.getCellNodeBox(row, column); @@ -733,6 +778,283 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On } + private createNewTable(): void { + let newGridContainer = document.createElement('div'); + newGridContainer.className = 'grid'; + + if (this.placeHolderDataSets) { + let dataSet = this.placeHolderDataSets[0]; + let options = { + enableCellNavigation: true, + enableColumnReorder: false, + renderRowWithRange: true, + showHeader: true, + rowHeight: this.rowHeight, + defaultColumnWidth: 120, + defaultFormatter: undefined, + editable: this.enableEditing, + autoEdit: this.enableEditing, + enableAddRow: false, + enableAsyncPostRender: false, + editorFactory: { + getEditor: (column: ISlickColumn) => this.getColumnEditor(column) + } + }; + + if (dataSet.columnDefinitions) { + this.table = new Table(this.nativeElement.appendChild(newGridContainer), { dataProvider: this.gridDataProvider, columns: dataSet.columnDefinitions }, options); + for (let plugin of this.plugins) { + this.table.registerPlugin(plugin); + } + for (let i = 0; i < dataSet.columnDefinitions.length; i++) { + this.columnNameToIndex[this.dataSet.columnDefinitions[i].name] = i; + } + } + } + else { + this.table = new Table(this.nativeElement.appendChild(newGridContainer)); + } + } + + getOverridableTextEditorClass(): any { + let self = this; + class OverridableTextEditor { + private _textEditor: any; + public keyCaptureList: number[]; + + constructor(private _args: any) { + this._textEditor = new Slick.Editors.Text(_args); + const END = 35; + const HOME = 36; + + // These are the special keys the text editor should capture instead of letting + // the grid handle them + this.keyCaptureList = [END, HOME]; + } + + destroy(): void { + this._textEditor.destroy(); + } + + focus(): void { + this._textEditor.focus(); + } + + getValue(): string { + return this._textEditor.getValue(); + } + + setValue(val): void { + this._textEditor.setValue(val); + } + + loadValue(item, rowNumber): void { + if (self.overrideCellFn) { + let overrideValue = self.overrideCellFn(rowNumber, this._args.column.id, item[this._args.column.id]); + if (overrideValue !== undefined) { + item[this._args.column.id] = overrideValue; + } + } + this._textEditor.loadValue(item); + } + + serializeValue(): string { + return this._textEditor.serializeValue(); + } + + applyValue(item, state): void { + let activeRow = self.currentCell.row; + let currentRow = self.dataSet.dataRows.at(activeRow); + let colIndex = self.getColumnIndex(this._args.column.name); + let dataLength: number = self.dataSet.dataRows.getLength(); + + // If this is not the "new row" at the very bottom + if (activeRow !== dataLength) { + currentRow[colIndex] = state; + this._textEditor.applyValue(item, state); + } + } + + isValueChanged(): boolean { + return this._textEditor.isValueChanged(); + } + + validate(): any { + let activeRow = self.currentCell.row; + let result: any = { valid: true, msg: undefined }; + let colIndex: number = self.getColumnIndex(this._args.column.name); + let newValue: any = this._textEditor.getValue(); + if (self.onIsCellEditValid && !self.onIsCellEditValid(activeRow, colIndex, newValue)) { + result.valid = false; + } + + return result; + } + } + + return OverridableTextEditor; + } + + private getColumnEditor(column: ISlickColumn): any { + if (column.isEditable === false || typeof column.isEditable === 'undefined') { + return undefined; + } + let columnId = column.id; + let canEditColumn = columnId !== undefined; + if (canEditColumn) { + return this.getOverridableTextEditorClass(); + } + return undefined; + } + + public getColumnIndex(name: string): number { + return this.columnNameToIndex[name]; + } + + handleChanges(changes: { [propName: string]: any }): void { + let columnDefinitionChanges = changes['columnDefinitions']; + let activeCell = this.table ? this.table.grid.getActiveCell() : undefined; + let hasGridStructureChanges = false; + let wasEditing = this.table ? !!this.table.grid.getCellEditor() : false; + + if (columnDefinitionChanges && !equals(columnDefinitionChanges.previousValue, columnDefinitionChanges.currentValue)) { + if (!this.table) { + this.createNewTable(); + } else { + this.table.grid.resetActiveCell(); + this.table.grid.setColumns(this.dataSet.columnDefinitions); + } + hasGridStructureChanges = true; + + if (!columnDefinitionChanges.currentValue || columnDefinitionChanges.currentValue.length === 0) { + activeCell = undefined; + } + if (activeCell) { + let columnThatContainedActiveCell = columnDefinitionChanges.previousValue[Math.max(activeCell.cell - 1, 0)]; + let newActiveColumnIndex = columnThatContainedActiveCell + ? columnDefinitionChanges.currentValue.findIndex(c => c.id === columnThatContainedActiveCell.id) + : -1; + activeCell.cell = newActiveColumnIndex !== -1 ? newActiveColumnIndex + 1 : 0; + } + } + + if (changes['dataRows'] + || (changes['highlightedCells'] && !equals(changes['highlightedCells'].currentValue, changes['highlightedCells'].previousValue)) + || (changes['blurredColumns'] && !equals(changes['blurredColumns'].currentValue, changes['blurredColumns'].previousValue)) + || (changes['columnsLoading'] && !equals(changes['columnsLoading'].currentValue, changes['columnsLoading'].previousValue))) { + this.setCallbackOnDataRowsChanged(); + this.table.rerenderGrid(0, this.dataSet.dataRows.getLength()); + hasGridStructureChanges = true; + } + + if (hasGridStructureChanges) { + if (activeCell) { + this.table.grid.setActiveCell(activeCell.row, activeCell.cell); + } else { + this.table.grid.resetActiveCell(); + } + } + + if (wasEditing && hasGridStructureChanges) { + this.table.grid.editActiveCell(this.table.grid.getCellEditor()); + } + } + + private setCallbackOnDataRowsChanged(): void { + //check if dataRows exist before we enable editing or slickgrid will complain + if (this.dataSet.dataRows) { + this.changeEditSession(true); + this.dataSet.dataRows.setCollectionChangedCallback((startIndex: number, count: number) => { + this.renderGridDataRowsRange(startIndex, count); + }); + } + } + + private changeEditSession(enabled: boolean): void { + this.enableEditing = enabled; + let options: any = this.table.grid.getOptions(); + options.editable = enabled; + options.enableAddRow = false; + this.table.grid.setOptions(options); + } + + private renderGridDataRowsRange(startIndex: number, count: number): void { + let editor = >this.table.grid.getCellEditor(); + let oldValue = editor ? editor.getValue() : undefined; + let wasValueChanged = editor ? editor.isValueChanged() : false; + this.invalidateRange(startIndex, startIndex + count); + let activeCell = this.currentCell; + if (editor && activeCell.row >= startIndex && activeCell.row < startIndex + count) { + if (oldValue && wasValueChanged) { + editor.setValue(oldValue); + } + } + } + + private invalidateRange(start: number, end: number): void { + let refreshedRows = Array.from({ length: (end - start) }, (v, k) => k + start); + this.table.grid.invalidateRows(refreshedRows, true); + this.table.grid.render(); + } + + private setupEvents(): void { + this.table.grid.onScroll.subscribe((e, args) => { + this.onScroll(args); + }); + this.table.grid.onCellChange.subscribe((e, args) => { + this.onCellEditEnd(args); + }); + this.table.grid.onBeforeEditCell.subscribe((e, args) => { + this.onBeforeEditCell(args); + }); + // Subscribe to all active cell changes to be able to catch when we tab to the header on the next row + this.table.grid.onActiveCellChanged.subscribe((e, args) => { + // Emit that we've changed active cells + this.onActiveCellChanged(args); + }); + this.table.grid.onContextMenu.subscribe((e, args) => { + this.openContextMenu(e, this.dataSet.batchId, this.dataSet.resultId, 0); + }); + this.table.grid.onBeforeAppendCell.subscribe((e, args) => { + // Since we need to return a string here, we are using calling a function instead of event emitter like other events handlers + return this.onBeforeAppendCell ? this.onBeforeAppendCell(args.row, args.cell) : undefined; + }); + this.table.grid.onRendered.subscribe((e, args) => { + this.onGridRendered(args); + }); + } + + onBeforeEditCell(event: Slick.OnBeforeEditCellEventArgs): void { + this.logService.debug('onBeforeEditCell called with grid: ' + event.grid + ' row: ' + event.row + + ' cell: ' + event.cell + ' item: ' + event.item + ' column: ' + event.column); + } + + handleInitializeTable(): void { + // handleInitializeTable() will be called *after* the first time handleChanges() is called + // so, grid must be there already + + if (this.dataSet.dataRows && this.dataSet.dataRows.getLength() > 0) { + this.table.grid.scrollRowToTop(0); + } + + if (this.dataSet.resized) { + // Re-rendering the grid is expensive. Throttle so we only do so every 100ms. + this.dataSet.resized.throttleTime(100) + .subscribe(() => this.onResize()); + } + + // subscribe to slick events + // https://github.com/mleibman/SlickGrid/wiki/Grid-Events + this.setupEvents(); + } + + private onResize(): void { + if (this.table.grid !== undefined) { + // this will make sure the grid header and body to be re-rendered + this.table.grid.resizeCanvas(); + } + } + /*Formatter for Column*/ private getColumnFormatter(row: number | undefined, cell: any | undefined, value: any, columnDef: any | undefined, dataContext: any | undefined): string { let valueToDisplay = ''; diff --git a/src/sql/workbench/contrib/editData/browser/editDataResultsEditor.ts b/src/sql/workbench/contrib/editData/browser/editDataResultsEditor.ts index 2faa8fde4f..57c2f4ff3c 100644 --- a/src/sql/workbench/contrib/editData/browser/editDataResultsEditor.ts +++ b/src/sql/workbench/contrib/editData/browser/editDataResultsEditor.ts @@ -15,11 +15,8 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import * as types from 'vs/base/common/types'; import { IQueryModelService } from 'sql/platform/query/common/queryModel'; -import { bootstrapAngular } from 'sql/workbench/services/bootstrap/browser/bootstrapService'; -import { BareResultsGridInfo } from 'sql/workbench/contrib/query/browser/queryResultsEditor'; -import { IEditDataComponentParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; -import { EditDataModule } from 'sql/workbench/contrib/editData/browser/editData.module'; -import { EDITDATA_SELECTOR } from 'sql/workbench/contrib/editData/browser/editData.component'; +import { BareResultsGridInfo, getBareResultsGridInfoStyles } from 'sql/workbench/contrib/query/browser/queryResultsEditor'; +import { EditDataGridPanel } from 'sql/workbench/contrib/editData/browser/editDataGridPanel'; import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -31,6 +28,8 @@ export class EditDataResultsEditor extends BaseEditor { protected _input: EditDataResultsInput; protected _rawOptions: BareResultsGridInfo; + private styleSheet = DOM.createStyleSheet(); + constructor( @ITelemetryService telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @@ -54,9 +53,11 @@ export class EditDataResultsEditor extends BaseEditor { } public createEditor(parent: HTMLElement): void { + parent.appendChild(this.styleSheet); } public dispose(): void { + this.styleSheet = undefined; super.dispose(); } @@ -67,7 +68,7 @@ export class EditDataResultsEditor extends BaseEditor { super.setInput(input, options, CancellationToken.None); this._applySettings(); if (!input.hasBootstrapped) { - this._bootstrapAngular(); + this.createGridPanel(); } return Promise.resolve(null); } @@ -85,41 +86,26 @@ export class EditDataResultsEditor extends BaseEditor { cssRuleText = this._rawOptions.cellPadding.join('px ') + 'px;'; } let content = `.grid .slick-cell { padding: ${cssRuleText}; }`; + content += `.grid-panel .monaco-table, .message-tree { ${getBareResultsGridInfoStyles(this._rawOptions)} }`; this.input.css.innerHTML = content; } } - /** - * Load the angular components and record for this input that we have done so - */ - private _bootstrapAngular(): void { + private createGridPanel(): void { let input = this.input; let uri = input.uri; - // Pass the correct DataService to the new angular component let dataService = this._queryModelService.getDataService(uri); if (!dataService) { throw new Error('DataService not found for URI: ' + uri); } - // Mark that we have bootstrapped input.setBootstrappedTrue(); - - // Get the bootstrap params and perform the bootstrap // Note: pass in input so on disposal this is cleaned up. // Otherwise many components will be left around and be subscribed // to events from the backing data service - const parent = input.container; - let params: IEditDataComponentParams = { - dataService: dataService, - onSaveViewState: input.onSaveViewStateEmitter.event, - onRestoreViewState: input.onRestoreViewStateEmitter.event - }; - bootstrapAngular(this._instantiationService, - EditDataModule, - parent, - EDITDATA_SELECTOR, - params, - input); + this._applySettings(); + let editGridPanel = this._register(this._instantiationService.createInstance(EditDataGridPanel, dataService, input.onSaveViewStateEmitter.event, input.onRestoreViewStateEmitter.event)); + editGridPanel.render(this.getContainer()); } } diff --git a/src/sql/workbench/contrib/editData/browser/gridParentComponent.ts b/src/sql/workbench/contrib/editData/browser/gridParentComponent.ts index b92ebcef52..f55e798314 100644 --- a/src/sql/workbench/contrib/editData/browser/gridParentComponent.ts +++ b/src/sql/workbench/contrib/editData/browser/gridParentComponent.ts @@ -6,9 +6,8 @@ import 'vs/css!./media/flexbox'; import 'vs/css!./media/styles'; +import { Table } from 'sql/base/browser/ui/table/table'; import { Subscription, Subject } from 'rxjs/Rx'; -import { ElementRef, QueryList, ChangeDetectorRef, ViewChildren } from '@angular/core'; -import { SlickGrid } from 'angular2-slickgrid'; import * as Constants from 'sql/workbench/contrib/query/common/constants'; import * as LocalizedConstants from 'sql/workbench/contrib/query/common/localizedConstants'; import { IGridInfo, IGridDataSet, SaveFormat } from 'sql/workbench/contrib/grid/common/interfaces'; @@ -25,20 +24,21 @@ import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ILogService } from 'vs/platform/log/common/log'; import { subscriptionToDisposable } from 'sql/base/browser/lifecycle'; -export abstract class GridParentComponent { + +export abstract class GridParentComponent extends Disposable { // CONSTANTS // tslint:disable:no-unused-variable protected get selectionModel() { return new CellSelectionModel(); } - protected _rowHeight = 29; - protected _defaultNumShowingRows = 8; + protected rowHeight = 29; + protected defaultNumShowingRows = 8; protected Constants = Constants; protected LocalizedConstants = LocalizedConstants; protected Utils = Utils; @@ -56,7 +56,6 @@ export abstract class GridParentComponent { protected toDispose = new DisposableStore(); - // Context keys to set when keybindings are available private resultsVisibleContextKey: IContextKey; private gridFocussedContextKey: IContextKey; @@ -69,34 +68,32 @@ export abstract class GridParentComponent { // Datasets currently being rendered on the DOM protected renderedDataSets: IGridDataSet[] = this.placeHolderDataSets; protected resultActive = true; - protected _messageActive = true; + protected messageActiveBool = true; protected activeGrid = 0; - - @ViewChildren('slickgrid') slickgrids: QueryList; + protected nativeElement: HTMLElement; + protected table: Table; set messageActive(input: boolean) { - this._messageActive = input; + this.messageActiveBool = input; if (this.resultActive) { this.resizeGrids(); } - this._cd.detectChanges(); } get messageActive(): boolean { - return this._messageActive; + return this.messageActiveBool; } constructor( - protected _el: ElementRef, - protected _cd: ChangeDetectorRef, - protected contextMenuService: IContextMenuService, - protected keybindingService: IKeybindingService, - protected contextKeyService: IContextKeyService, - protected configurationService: IConfigurationService, - protected clipboardService: IClipboardService, - protected queryEditorService: IQueryEditorService, - protected logService: ILogService + @IContextMenuService protected contextMenuService: IContextMenuService, + @IKeybindingService protected keybindingService: IKeybindingService, + @IContextKeyService protected contextKeyService: IContextKeyService, + @IConfigurationService protected configurationService: IConfigurationService, + @IClipboardService protected clipboardService: IClipboardService, + @IQueryEditorService protected queryEditorService: IQueryEditorService, + @ILogService protected logService: ILogService ) { + super(); } protected baseInit(): void { @@ -105,13 +102,13 @@ export abstract class GridParentComponent { if (this.configurationService) { let sqlConfig = this.configurationService.getValue('sql'); if (sqlConfig) { - this._messageActive = sqlConfig['messagesDefaultOpen']; + this.messageActiveBool = sqlConfig['messagesDefaultOpen']; } } this.subscribeWithDispose(this.dataService.gridContentObserver, (type) => { switch (type) { case GridContentEvents.RefreshContents: - self.refreshResultsets(); + self.refreshDatasets(); break; case GridContentEvents.ResizeContents: self.resizeGrids(); @@ -184,7 +181,7 @@ export abstract class GridParentComponent { this.queryEditorVisible = QueryEditorVisibleContext.bindTo(contextKeyService); this.queryEditorVisible.set(true); - let gridContextKeyService = this.contextKeyService.createScoped(this._el.nativeElement); + let gridContextKeyService = this.contextKeyService.createScoped(this.nativeElement); this.toDispose.add(gridContextKeyService); this.resultsVisibleContextKey = ResultsVisibleContext.bindTo(gridContextKeyService); this.resultsVisibleContextKey.set(true); @@ -203,7 +200,6 @@ export abstract class GridParentComponent { if (this.resultActive) { this.resizeGrids(); } - this._cd.detectChanges(); } protected toggleMessagePane(): void { @@ -227,7 +223,7 @@ export abstract class GridParentComponent { } protected getSelection(index?: number): Slick.Range[] { - let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges(); + let selection = this.table.getSelectedRanges(); if (selection) { selection = selection.map(c => { return { fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; }); return selection; @@ -380,13 +376,11 @@ export abstract class GridParentComponent { } openContextMenu(event, batchId, resultId, index): void { - let slick: any = this.slickgrids.toArray()[index]; - let grid = slick._grid; - + let grid = this.table.grid; let selection = this.getSelection(index); if (selection && selection.length === 0) { - let cell = (grid as Slick.Grid).getCellFromEvent(event); + let cell = grid.getCellFromEvent(event); selection = [new Slick.Range(cell.row, cell.cell - 1)]; } @@ -421,16 +415,14 @@ export abstract class GridParentComponent { let self = this; return (gridIndex: number) => { self.activeGrid = gridIndex; - let grid = self.slickgrids.toArray()[self.activeGrid]; + let grid = self.table; grid.setActive(); - grid.selection = true; + grid.setSelectedRows(true); }; } private onSelectAllForActiveGrid(): void { - if (this.activeGrid >= 0 && this.slickgrids.length > this.activeGrid) { - this.slickgrids.toArray()[this.activeGrid].selection = true; - } + this.table.setSelectedRows(true); } /** @@ -447,29 +439,27 @@ export abstract class GridParentComponent { } setTimeout(() => { self.resizeGrids(); - self.slickgrids.toArray()[0].setActive(); - self._cd.detectChanges(); + self.table.setActive(); }); } abstract onScroll(scrollTop): void; protected getResultsElement(): any { - return this._el.nativeElement.querySelector('#results'); + return this.nativeElement.querySelector('#results'); } protected getMessagesElement(): any { - return this._el.nativeElement.querySelector('#messages'); + return this.nativeElement.querySelector('#messages'); } /** - * Force angular to re-render the results grids. Calling this upon unhide (upon focus) fixes UI + * Force re-rendering of the results grids. Calling this upon unhide (upon focus) fixes UI * glitches that occur when a QueryRestulsEditor is hidden then unhidden while it is running a query. + * Detect Changes has been moved to editDataGridPanel version of this. */ - refreshResultsets(): void { + refreshDatasets(): void { let tempRenderedDataSets = this.renderedDataSets; this.renderedDataSets = []; - this._cd.detectChanges(); this.renderedDataSets = tempRenderedDataSets; - this._cd.detectChanges(); } getSelectedRangeUnderMessages(): Selection { @@ -481,8 +471,8 @@ export abstract class GridParentComponent { } selectAllMessages(): void { - let msgEl = this._el.nativeElement.querySelector('#messages'); - this.selectElementContents(msgEl); + let msgEl = this.nativeElement.querySelector('#messages'); + this.selectElementContents(msgEl); } selectElementContents(el: HTMLElement): void { @@ -513,10 +503,15 @@ export abstract class GridParentComponent { const self = this; setTimeout(() => { for (let grid of self.renderedDataSets) { - grid.resized.emit(); + grid.resized.fire(); } }); } - // Private Helper Functions //////////////////////////////////////////////////////////////////////////// + /** + * used to render the native element into the container. + * */ + public render(container: HTMLElement): void { + container.appendChild(this.nativeElement); + } } diff --git a/src/sql/workbench/contrib/editData/browser/media/editData.css b/src/sql/workbench/contrib/editData/browser/media/editData.css index f9e7461ad9..f43c425a9c 100644 --- a/src/sql/workbench/contrib/editData/browser/media/editData.css +++ b/src/sql/workbench/contrib/editData/browser/media/editData.css @@ -3,11 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.editdata-component * { +.editdatagridpanel * { box-sizing: border-box; } #workbench\.editor\.editDataEditor .monaco-toolbar .monaco-select-box { margin-top: 4px; margin-bottom: 4px; -} \ No newline at end of file +} diff --git a/src/sql/workbench/contrib/grid/common/dataService.ts b/src/sql/workbench/contrib/grid/common/dataService.ts index ca1c3bde87..3f06e8a634 100644 --- a/src/sql/workbench/contrib/grid/common/dataService.ts +++ b/src/sql/workbench/contrib/grid/common/dataService.ts @@ -149,7 +149,7 @@ export class DataService { this._queryModel.copyResults(this._uri, selection, batchId, resultId, includeHeaders); } - onAngularLoaded(): void { - this._queryModel.onAngularLoaded(this._uri); + onLoaded(): void { + this._queryModel.onLoaded(this._uri); } } diff --git a/src/sql/workbench/contrib/grid/common/interfaces.ts b/src/sql/workbench/contrib/grid/common/interfaces.ts index 72e163bf1d..0a4b4fd12f 100644 --- a/src/sql/workbench/contrib/grid/common/interfaces.ts +++ b/src/sql/workbench/contrib/grid/common/interfaces.ts @@ -3,8 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISlickColumn, VirtualizedCollection } from 'angular2-slickgrid'; - +import { VirtualizedCollection, ISlickColumn } from 'sql/base/browser/ui/table/asyncDataView'; export interface IGridDataSet { dataRows: VirtualizedCollection<{}>; columnDefinitions: ISlickColumn[]; diff --git a/src/sql/workbench/services/bootstrap/common/bootstrapParams.ts b/src/sql/workbench/services/bootstrap/common/bootstrapParams.ts index 50d445f9fc..8f6f207690 100644 --- a/src/sql/workbench/services/bootstrap/common/bootstrapParams.ts +++ b/src/sql/workbench/services/bootstrap/common/bootstrapParams.ts @@ -16,12 +16,6 @@ export interface IQueryComponentParams extends IBootstrapParams { onRestoreViewState: Event; } -export interface IEditDataComponentParams extends IBootstrapParams { - dataService: DataService; - onSaveViewState: Event; - onRestoreViewState: Event; -} - export interface IDefaultComponentParams extends IBootstrapParams { connection: IConnectionProfile; ownerUri: string; diff --git a/src/typings/slickgrid.d.ts b/src/typings/slickgrid.d.ts index 6bfb855462..cdba93425c 100644 --- a/src/typings/slickgrid.d.ts +++ b/src/typings/slickgrid.d.ts @@ -1192,6 +1192,7 @@ declare namespace Slick { public onHeaderContextMenu: Slick.Event>; public onHeaderClick: Slick.Event>; public onHeaderCellRendered: Slick.Event>; + public onBeforeAppendCell: Slick.Event>; public onBeforeHeaderCellDestroy: Slick.Event>; public onHeaderRowCellRendered: Slick.Event>; public onBeforeHeaderRowCellDestroy: Slick.Event>; @@ -1305,6 +1306,11 @@ declare namespace Slick { cell: number; } + export interface OnBeforeAppendCellArgs extends GridEventArgs { + row: number; + cell: number; + } + export interface OnBeforeDestroyEventArgs extends GridEventArgs { } diff --git a/yarn.lock b/yarn.lock index ab4512f553..845b941cf9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -726,10 +726,6 @@ angular2-grid@2.0.6: resolved "https://registry.yarnpkg.com/angular2-grid/-/angular2-grid-2.0.6.tgz#01fe225dc13b2822370b6c61f9a6913b3a26f989" integrity sha1-Af4iXcE7KCI3C2xh+aaROzom+Yk= -"angular2-slickgrid@github:Microsoft/angular2-slickgrid#1.4.6": - version "1.4.6" - resolved "https://codeload.github.com/Microsoft/angular2-slickgrid/tar.gz/09579fdc90b1ec469578ec1040d1515585ce2a11" - ansi-colors@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9"