Rework slickgrid keyboard navigation (#1930)

* rewrite keybind nav to handle ctrl + home and end

* testing different options

* working on removed slickgrid changes we don't need

* formatting

* handle click handler to rowNumber

* fixing various bugs

* formatting

* readd click column to select

* add shift key to column select

* added logic for additional keybindings on grid

* add down and up arrow into keyboard navigation

* update styling and update slickgrid

* formatting

* update angular-slickgrid version

* remove index.js changes
This commit is contained in:
Anthony Dresser
2018-07-19 14:05:54 -07:00
committed by GitHub
parent a64a0d1db6
commit ce878e1def
16 changed files with 380 additions and 434 deletions

View File

@@ -11,8 +11,8 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Table } from 'sql/base/browser/ui/table/table';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { attachTableStyler} from 'sql/common/theme/styler';
import { attachTableStyler } from 'sql/common/theme/styler';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
@Component({
template: ''
@@ -63,7 +63,7 @@ export default class TableInsight extends Disposable implements IInsightsView, O
private createTable() {
if (!this.table) {
this.table = new Table(this._elementRef.nativeElement, this.dataView, this.columns, { showRowNumber: true });
this.table.setSelectionModel(new DragCellSelectionModel());
this.table.setSelectionModel(new CellSelectionModel());
this._register(attachTableStyler(this.table, this.themeService));
}
}

View File

@@ -4,7 +4,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IColumnDefinition, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
import { ISlickColumn, IObservableCollection, IGridDataRow } from 'angular2-slickgrid';
export interface ISlickRange {
fromCell: number;
@@ -42,7 +42,7 @@ export interface IGridIcon {
export interface IGridDataSet {
dataRows: IObservableCollection<IGridDataRow>;
columnDefinitions: IColumnDefinition[];
columnDefinitions: ISlickColumn<any>[];
resized: any; // EventEmitter<any>;
totalRows: number;
batchId: number;

View File

@@ -17,14 +17,13 @@
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
[plugins]="slickgridPlugins"
[plugins]="plugins[i]"
(activeCellChanged)="onActiveCellChanged($event)"
(cellEditBegin)="onCellEditBegin($event)"
(cellEditExit)="onCellEditEnd($event)"
(rowEditBegin)="onRowEditBegin($event)"
(rowEditExit)="onRowEditEnd($event)"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
[isColumnEditable]="onIsColumnEditable"
[isCellEditValid]="onIsCellEditValid"
[overrideCellFn]="overrideCellFn"
enableEditing="true"

View File

@@ -24,6 +24,9 @@ import { error } from 'sql/base/common/log';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
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 { INotificationService } from 'vs/platform/notification/common/notification';
@@ -36,7 +39,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export const EDITDATA_SELECTOR: string = 'editdata-component';
@Component({
@@ -66,6 +68,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
private newRowVisible: boolean;
private removingNewRow: boolean;
private rowIdMappings: { [gridRowId: number]: number } = {};
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// Edit Data functions
public onActiveCellChanged: (event: { row: number, column: number }) => void;
@@ -167,17 +170,6 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.onRowEditEnd = (event: { row: number }): void => { };
this.onIsColumnEditable = (column: number): boolean => {
let result = false;
// Check that our variables exist
if (column !== undefined && !!this.dataSet && !!this.dataSet.columnDefinitions[column]) {
result = this.dataSet.columnDefinitions[column].isEditable;
}
// If no column definition exists then the row is not editable
return result;
};
this.overrideCellFn = (rowNumber, columnId, value?, data?): string => {
let returnVal = '';
if (Services.DBCellValue.isDBCellValue(value)) {
@@ -197,9 +189,9 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
self.idMapping[rowIndex] = row.id;
rowIndex++;
return {
values: row.cells.map(c => {
values: [{}].concat(row.cells.map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
}), row: row.id
})), row: row.id
};
});
@@ -351,6 +343,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
let maxHeight = this.getMaxHeight(resultSet.rowCount);
let minHeight = this.getMinHeight(resultSet.rowCount);
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
@@ -365,7 +359,7 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this.loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: resultSet.columnInfo.map((c, i) => {
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
@@ -374,13 +368,14 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
type: self.stringToFieldType('string'),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined,
isEditable: c.isUpdatable
};
})
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSet = dataSet;
// Create a dataSet to render without rows to reduce DOM size

View File

@@ -26,6 +26,7 @@ import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
import { ResultsVisibleContext, ResultsGridFocussedContext, ResultsMessagesFocussedContext, QueryEditorVisibleContext } from 'sql/parts/query/common/queryContext';
import { error } from 'sql/base/common/log';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
import { IAction } from 'vs/base/common/actions';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
@@ -33,8 +34,6 @@ 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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { DragCellSelectionModel } from 'sql/base/browser/ui/table/plugins/dragCellSelectionModel.plugin';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -42,14 +41,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export abstract class GridParentComponent {
// CONSTANTS
// tslint:disable:no-unused-variable
protected get selectionModel(): DragCellSelectionModel<any> {
return new DragCellSelectionModel<any>();
}
protected get slickgridPlugins(): Array<any> {
return [
new AutoColumnSize<any>({})
];
}
protected get selectionModel() { return new CellSelectionModel(); }
protected _rowHeight = 29;
protected _defaultNumShowingRows = 8;
protected Constants = Constants;
@@ -94,7 +87,6 @@ export abstract class GridParentComponent {
public onRowEditBegin: (event: { row: number }) => void;
public onRowEditEnd: (event: { row: number }) => void;
public onIsCellEditValid: (row: number, column: number, newValue: any) => boolean;
public onIsColumnEditable: (column: number) => boolean;
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
@@ -423,7 +415,9 @@ export abstract class GridParentComponent {
let self = this;
return (gridIndex: number) => {
self.activeGrid = gridIndex;
self.slickgrids.toArray()[this.activeGrid].selection = true;
let grid = self.slickgrids.toArray()[self.activeGrid];
grid.setActive();
grid.selection = true;
};
}
@@ -433,22 +427,6 @@ export abstract class GridParentComponent {
}
}
/**
* Used to convert the string to a enum compatible with SlickGrid
*/
protected stringToFieldType(input: string): FieldType {
let fieldtype: FieldType;
switch (input) {
case 'string':
fieldtype = FieldType.String;
break;
default:
fieldtype = FieldType.String;
break;
}
return fieldtype;
}
/**
* Makes a resultset take up the full result height if this is not already true
* Otherwise rerenders the result sets from default

View File

@@ -20,12 +20,11 @@
[dataRows]="dataSet.dataRows"
(contextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
enableAsyncPostRender="true"
showDataTypeIcon="false"
showHeader="true"
[resized]="dataSet.resized"
(mousedown)="navigateToGrid(i)"
[selectionModel]="selectionModel"
[plugins]="slickgridPlugins"
[plugins]="plugins[i]"
class="boxCol content vertBox slickgrid"
[rowHeight]="rowHeight">
</slick-grid>

View File

@@ -28,6 +28,9 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { escape } from 'sql/base/common/strings';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { format } from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom';
@@ -61,7 +64,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
// create a function alias to use inside query.component
// tslint:disable-next-line:no-unused-variable
private stringsFormat: any = format;
protected stringsFormat: any = format;
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// tslint:disable-next-line:no-unused-variable
private dataIcons: IGridIcon[] = [
@@ -302,9 +307,9 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
for (let row = 0; row < rows.rows.length; row++) {
// Push row values onto end of gridData for slickgrid
gridData.push({
values: rows.rows[row].map(c => {
values: [{}].concat(rows.rows[row].map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
})
}))
});
}
@@ -331,6 +336,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
minHeight = minHeightNumber.toString() + 'px';
}
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
@@ -345,7 +352,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: resultSet.columnInfo.map((c, i) => {
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
@@ -354,12 +361,13 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
type: self.stringToFieldType('string'),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
};
})
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSets.push(dataSet);
// check if the resultset is for a query plan