Add no implicit any to the strict null check (#5635)

* wip

* working through adding no implicit any
This commit is contained in:
Anthony Dresser
2019-06-04 09:29:40 -07:00
committed by GitHub
parent 50242b2c35
commit 4ad226570a
26 changed files with 296 additions and 306 deletions

View File

@@ -222,4 +222,8 @@ export class AsyncDataProvider<T extends Slick.SlickData> implements IDisposable
dispose() {
this.dataRows.dispose();
}
getItems(): T[] {
throw new Error('Method not supported.');
}
}

View File

@@ -15,7 +15,7 @@ export class AdditionalKeyBindings<T> implements Slick.Plugin<T> {
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler.subscribe(this.grid.onKeyDown, (e, args) => this.handleKeyDown(e, args));
this.handler.subscribe(this.grid.onKeyDown, (e: KeyboardEvent, args) => this.handleKeyDown(e, args));
}
public destroy() {

View File

@@ -49,7 +49,7 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
// since data can be async we want to only do this if we have the data to actual
// work on since we are measuring the physical length of data
let data = this._grid.getData();
let data = this._grid.getData() as Slick.DataProvider<T>;
let item = data.getItem(0);
if (item && Object.keys(item).length > 0) {
let hasValue = false;
@@ -134,15 +134,15 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
}
}
private getMaxColumnTextWidth(columnDef, colIndex: number): number {
private getMaxColumnTextWidth(columnDef: Slick.Column<T>, colIndex: number): number {
let texts: Array<string> = [];
let rowEl = this.createRow(columnDef);
let data = this._grid.getData();
let rowEl = this.createRow();
let data = this._grid.getData() as Slick.DataProvider<T>;
let viewPort = this._grid.getViewport();
let start = Math.max(0, viewPort.top);
let end = Math.min(data.getLength(), viewPort.bottom);
for (let i = start; i < end; i++) {
texts.push(data.getItem(i)[columnDef.field]);
texts.push(data.getItem(i)[columnDef.field!]);
}
let template = this.getMaxTextTemplate(texts, columnDef, colIndex, data, rowEl);
let width = this.getTemplateWidth(rowEl, template);
@@ -150,21 +150,21 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
return width > this._options.maxWidth! ? this._options.maxWidth! : width;
}
private getTemplateWidth(rowEl: JQuery, template: JQuery | HTMLElement): number {
private getTemplateWidth(rowEl: JQuery, template: JQuery | HTMLElement | string): number {
let cell = jQuery(rowEl.find('.slick-cell'));
cell.append(template);
jQuery(cell).find('*').css('position', 'relative');
return cell.outerWidth() + 1;
}
private getMaxTextTemplate(texts: string[], columnDef, colIndex: number, data, rowEl: JQuery): JQuery | HTMLElement {
private getMaxTextTemplate(texts: string[], columnDef: Slick.Column<T>, colIndex: number, data: Slick.DataProvider<T>, rowEl: JQuery): JQuery | HTMLElement | string {
let max = 0,
maxTemplate = null;
maxTemplate: JQuery | HTMLElement | string | undefined;
let formatFun = columnDef.formatter;
texts.forEach((text, index) => {
let template;
if (formatFun) {
template = jQuery('<span>' + formatFun(index, colIndex, text, columnDef, data[index]) + '</span>');
template = jQuery('<span>' + formatFun(index, colIndex, text, columnDef, data.getItem(index)) + '</span>');
text = template.text() || text;
}
let length = text ? this.getElementWidthUsingCanvas(rowEl, text) : 0;
@@ -176,7 +176,7 @@ export class AutoColumnSize<T> implements Slick.Plugin<T> {
return maxTemplate!;
}
private createRow(columnDef): JQuery {
private createRow(): JQuery {
let rowEl = jQuery('<div class="slick-row"><div class="slick-cell"></div></div>');
rowEl.find('.slick-cell').css({
'visibility': 'hidden',

View File

@@ -26,13 +26,13 @@ export interface ICellRangeSelectorOptions {
}
export interface ICellRangeSelector<T> extends Slick.Plugin<T> {
onCellRangeSelected: Slick.Event<{ range: Slick.Range }>;
onCellRangeSelected: Slick.Event<Slick.Range>;
onBeforeCellRangeSelected: Slick.Event<Slick.Cell>;
}
export interface ICellRangeDecorator {
show(range: Slick.Range);
hide();
show(range: Slick.Range): void;
hide(): void;
}
export class CellRangeSelector<T> implements ICellRangeSelector<T> {
@@ -44,7 +44,7 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
private currentlySelectedRange: { start: Slick.Cell, end?: Slick.Cell };
public onBeforeCellRangeSelected = new Slick.Event<Slick.Cell>();
public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>();
public onCellRangeSelected = new Slick.Event<Slick.Range>();
constructor(private options: ICellRangeSelectorOptions) {
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
@@ -58,9 +58,9 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
this.canvas = this.grid.getCanvasNode();
this.handler
.subscribe(this.grid.onDragInit, e => this.handleDragInit(e))
.subscribe(this.grid.onDragStart, (e, dd) => this.handleDragStart(e, dd))
.subscribe(this.grid.onDrag, (e, dd) => this.handleDrag(e, dd))
.subscribe(this.grid.onDragEnd, (e, dd) => this.handleDragEnd(e, dd));
.subscribe(this.grid.onDragStart, (e: MouseEvent, dd) => this.handleDragStart(e, dd))
.subscribe(this.grid.onDrag, (e: MouseEvent, dd) => this.handleDrag(e, dd))
.subscribe(this.grid.onDragEnd, (e: MouseEvent, dd) => this.handleDragEnd(e, dd));
}
public destroy() {
@@ -138,13 +138,11 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
if (!dd || !dd.range || !dd.range.start || !dd.range.end) {
return;
}
this.onCellRangeSelected.notify({
range: new Slick.Range(
dd.range.start.row,
dd.range.start.cell,
dd.range.end.row,
dd.range.end.cell
)
});
this.onCellRangeSelected.notify(new Slick.Range(
dd.range.start.row,
dd.range.start.cell,
dd.range.end.row,
dd.range.end.cell
));
}
}

View File

@@ -19,6 +19,7 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
private grid: Slick.Grid<T>;
private selector: ICellRangeSelector<T>;
private ranges: Array<Slick.Range> = [];
private _handler = new Slick.EventHandler();
public onSelectedRangesChanged = new Slick.Event<Array<Slick.Range>>();
@@ -35,19 +36,16 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.grid.onActiveCellChanged.subscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.subscribe(e => this.handleKeyDown(e));
this.grid.onHeaderClick.subscribe((e: MouseEvent, args) => this.handleHeaderClick(e, args));
this._handler.subscribe(this.grid.onActiveCellChanged, (e: Event, args: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, args));
this._handler.subscribe(this.grid.onKeyDown, (e: KeyboardEvent) => this.handleKeyDown(e));
this._handler.subscribe(this.grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
this.grid.registerPlugin(this.selector);
this.selector.onCellRangeSelected.subscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.subscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
this._handler.subscribe(this.selector.onCellRangeSelected, (e: Event, range: Slick.Range) => this.handleCellRangeSelected(e, range));
this._handler.subscribe(this.selector.onBeforeCellRangeSelected, (e: Event, cell: Slick.Cell) => this.handleBeforeCellRangeSelected(e, cell));
}
public destroy() {
this.grid.onActiveCellChanged.unsubscribe((e, args) => this.handleActiveCellChange(e, args));
this.grid.onKeyDown.unsubscribe(e => this.handleKeyDown(e));
this.selector.onCellRangeSelected.unsubscribe((e, args) => this.handleCellRangeSelected(e, args));
this.selector.onBeforeCellRangeSelected.unsubscribe((e, args) => this.handleBeforeCellRangeSelected(e, args));
this._handler.unsubscribeAll();
this.grid.unregisterPlugin(this.selector);
}
@@ -81,7 +79,7 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
return this.ranges;
}
private handleBeforeCellRangeSelected(e, args: Slick.Cell) {
private handleBeforeCellRangeSelected(e: Event, args: Slick.Cell) {
if (this.grid.getEditorLock().isActive()) {
e.stopPropagation();
return false;
@@ -89,12 +87,12 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
return true;
}
private handleCellRangeSelected(e, args: { range: Slick.Range }) {
this.grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);
this.setSelectedRanges([args.range]);
private handleCellRangeSelected(e: Event, range: Slick.Range) {
this.grid.setActiveCell(range.fromRow, range.fromCell, false, false, true);
this.setSelectedRanges([range]);
}
private handleActiveCellChange(e, args) {
private handleActiveCellChange(e: Event, args: Slick.OnActiveCellChangedEventArgs<T>) {
if (this.options.selectActiveCell && !isUndefinedOrNull(args.row) && !isUndefinedOrNull(args.cell)) {
this.setSelectedRanges([new Slick.Range(args.row, args.cell)]);
} else if (!this.options.selectActiveCell) {
@@ -120,7 +118,7 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
}
}
private handleKeyDown(e) {
private handleKeyDown(e: KeyboardEvent) {
/***
* Кey codes
* 37 left
@@ -128,12 +126,12 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
* 39 right
* 40 down
*/
let ranges, last;
let active = this.grid.getActiveCell();
let metaKey = e.ctrlKey || e.metaKey;
if (active && e.shiftKey && !metaKey && !e.altKey &&
(e.which === 37 || e.which === 39 || e.which === 38 || e.which === 40)) {
let ranges = this.getSelectedRanges(), last: Slick.Range;
ranges = this.getSelectedRanges();
if (!ranges.length) {
@@ -141,7 +139,7 @@ export class CellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slic
}
// keyboard can work with last range only
last = ranges.pop();
last = ranges.pop()!; // this is guarenteed since if ranges is empty we add one
// can't handle selection out of active cell
if (!last.contains(active.row, active.cell)) {

View File

@@ -8,6 +8,7 @@ import * as strings from 'vs/base/common/strings';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { range } from 'vs/base/common/arrays';
import * as dict from 'vs/base/common/collections';
export interface ICheckboxSelectColumnOptions extends Slick.PluginOptions, ICheckboxStyles {
columnId?: string;
@@ -44,11 +45,11 @@ const checkboxTemplate = `<div style="display: flex; align-items: center; flex-d
<input type="checkbox" {0}>
</div>`;
export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
export class CheckboxSelectColumn<T extends Slick.SlickData> implements Slick.Plugin<T> {
private _options: ICheckboxSelectColumnOptions;
private _grid: Slick.Grid<T>;
private _handler = new Slick.EventHandler();
private _selectedRowsLookup = {};
private _selectedRowsLookup: dict.INumberDictionary<boolean> = {};
private _selectedCheckBoxLookup = {};
private _useState = false;
@@ -64,10 +65,10 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler
.subscribe(this._grid.onSelectedRowsChanged, (e, args) => this.handleSelectedRowsChanged(e, args))
.subscribe(this._grid.onClick, (e, args) => this.handleClick(e, args))
.subscribe(this._grid.onHeaderClick, (e, args) => this.handleHeaderClick(e, args))
.subscribe(this._grid.onKeyDown, (e, args) => this.handleKeyDown(e, args));
.subscribe(this._grid.onSelectedRowsChanged, (e: Event, args: Slick.OnSelectedRowsChangedEventArgs<T>) => this.handleSelectedRowsChanged(e, args))
.subscribe(this._grid.onClick, (e: MouseEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(e, args))
.subscribe(this._grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args))
.subscribe(this._grid.onKeyDown, (e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>) => this.handleKeyDown(e, args));
}
public destroy(): void {
@@ -82,7 +83,7 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
}
const selectedRows = this._grid.getSelectedRows();
let lookup = {}, row, i;
let lookup: dict.INumberDictionary<boolean> = {}, row: number, i: number;
for (i = 0; i < selectedRows.length; i++) {
row = selectedRows[i];
lookup[row] = true;
@@ -91,9 +92,7 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
delete this._selectedRowsLookup[row];
}
}
for (i in this._selectedRowsLookup) {
this._grid.invalidateRow(i);
}
dict.forEach(this._selectedRowsLookup, (e) => this._grid.invalidateRow(e.key));
this._selectedRowsLookup = lookup;
this._grid.render();
@@ -231,11 +230,11 @@ export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
sortable: false,
cssClass: this._options.cssClass,
headerCssClass: this._options.headerCssClass,
formatter: (r, c, v, cd, dc) => this.checkboxSelectionFormatter(r, c, v, cd, dc)
formatter: (r, c, v, cd, dc) => this.checkboxSelectionFormatter(r, c, v, cd, dc as T)
};
}
private checkboxSelectionFormatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
private checkboxSelectionFormatter(row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string {
if (this.isCustomActionRequested()) {
return this.checkboxTemplateCustom(row);
}

View File

@@ -20,7 +20,7 @@ export class CopyKeybind<T> implements Slick.Plugin<T> {
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler.subscribe(this.grid.onKeyDown, (e, args) => this.handleKeyDown(e, args));
this.handler.subscribe(this.grid.onKeyDown, (e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>) => this.handleKeyDown(e, args));
}
public destroy() {

View File

@@ -1,46 +1,39 @@
// Adopted and converted to typescript from https://github.com/danny-sg/slickgrid-spreadsheet-plugins/blob/master/ext.headerfilter.js
// heavily modified
import { mixin } from 'vs/base/common/objects';
import { IButtonStyles } from 'vs/base/browser/ui/button/button';
import { localize } from 'vs/nls';
import { Button } from 'sql/base/browser/ui/button/button';
import { escape } from 'sql/base/common/strings';
export class HeaderFilter {
interface IExtendedColumn<T> extends Slick.Column<T> {
filterValues?: Array<string>;
}
export class HeaderFilter<T extends Slick.SlickData> {
public onFilterApplied = new Slick.Event();
public onCommand = new Slick.Event();
private grid: Slick.Grid<any>;
private grid: Slick.Grid<T>;
private handler = new Slick.EventHandler();
private defaults = {
filterImage: 'src/sql/media/icons/filter.svg',
sortAscImage: 'sort-asc.gif',
sortDescImage: 'sort-desc.gif'
};
private $menu?: JQuery<HTMLElement>;
private options: any;
private okButton: Button;
private clearButton: Button;
private cancelButton: Button;
private workingFilters: any;
private columnDef: any;
private workingFilters: Array<string>;
private columnDef: IExtendedColumn<T>;
private buttonStyles: IButtonStyles;
constructor(options: any) {
this.options = mixin(options, this.defaults, false);
}
public init(grid: Slick.Grid<any>): void {
public init(grid: Slick.Grid<T>): void {
this.grid = grid;
this.handler.subscribe(this.grid.onHeaderCellRendered, (e, args) => this.handleHeaderCellRendered(e, args))
.subscribe(this.grid.onBeforeHeaderCellDestroy, (e, args) => this.handleBeforeHeaderCellDestroy(e, args))
.subscribe(this.grid.onClick, (e) => this.handleBodyMouseDown)
this.handler.subscribe(this.grid.onHeaderCellRendered, (e: Event, args: Slick.OnHeaderCellRenderedEventArgs<T>) => this.handleHeaderCellRendered(e, args))
.subscribe(this.grid.onBeforeHeaderCellDestroy, (e: Event, args: Slick.OnBeforeHeaderCellDestroyEventArgs<T>) => this.handleBeforeHeaderCellDestroy(e, args))
.subscribe(this.grid.onClick, (e: MouseEvent) => this.handleBodyMouseDown(e))
.subscribe(this.grid.onColumnsResized, () => this.columnsResized())
.subscribe(this.grid.onKeyDown, (e) => this.handleKeyDown);
.subscribe(this.grid.onKeyDown, (e: KeyboardEvent) => this.handleKeyDown(e));
this.grid.setColumns(this.grid.getColumns());
jQuery(document.body).bind('mousedown', this.handleBodyMouseDown);
@@ -53,7 +46,7 @@ export class HeaderFilter {
jQuery(document.body).unbind('keydown', this.handleKeyDown);
}
private handleKeyDown = (e) => {
private handleKeyDown(e: KeyboardEvent | JQuery.Event<HTMLElement, null>): void {
if (this.$menu && (e.key === 'Escape' || e.keyCode === 27)) {
this.hideMenu();
e.preventDefault();
@@ -61,8 +54,8 @@ export class HeaderFilter {
}
}
private handleBodyMouseDown = (e) => {
if (this.$menu && this.$menu[0] !== e.target && !jQuery.contains(this.$menu[0], e.target)) {
private handleBodyMouseDown(e: MouseEvent | JQuery.Event<HTMLElement, null>): void {
if (this.$menu && this.$menu[0] !== e.target && !jQuery.contains(this.$menu[0], e.target as Element)) {
this.hideMenu();
e.preventDefault();
e.stopPropagation();
@@ -76,7 +69,7 @@ export class HeaderFilter {
}
}
private handleHeaderCellRendered(e, args) {
private handleHeaderCellRendered(e: Event, args: Slick.OnHeaderCellRenderedEventArgs<T>) {
const column = args.column;
if (column.id === '_detail_selector') {
return;
@@ -88,13 +81,13 @@ export class HeaderFilter {
$el.bind('click', (e) => this.showFilter(e)).appendTo(args.node);
}
private handleBeforeHeaderCellDestroy(e, args) {
private handleBeforeHeaderCellDestroy(e: Event, args: Slick.OnBeforeHeaderCellDestroyEventArgs<T>) {
jQuery(args.node)
.find('.slick-header-menubutton')
.remove();
}
private addMenuItem(menu, columnDef, title, command, image) {
private addMenuItem(menu: JQuery<HTMLElement>, columnDef: Slick.Column<T>, title: string, command: string) {
const $item = jQuery('<div class="slick-header-menuitem">')
.data('command', command)
.data('column', columnDef)
@@ -115,7 +108,7 @@ export class HeaderFilter {
.appendTo($item);
}
private addMenuInput(menu, columnDef) {
private addMenuInput(menu: JQuery<HTMLElement>, columnDef: Slick.Column<T>) {
const self = this;
jQuery('<input class="input" placeholder="Search" style="margin-top: 5px; width: 206px">')
.data('column', columnDef)
@@ -126,7 +119,7 @@ export class HeaderFilter {
.appendTo(menu);
}
private updateFilterInputs(menu, columnDef, filterItems) {
private updateFilterInputs(menu: JQuery<HTMLElement>, columnDef: IExtendedColumn<T>, filterItems: Array<string>) {
let filterOptions = '<label><input type="checkbox" value="-1" />(Select All)</label>';
columnDef.filterValues = columnDef.filterValues || [];
@@ -148,7 +141,7 @@ export class HeaderFilter {
});
}
private showFilter(e) {
private showFilter(e: JQuery.Event<HTMLElement, null>) {
const $menuButton = jQuery(e.target);
this.columnDef = $menuButton.data('column');
@@ -157,15 +150,15 @@ export class HeaderFilter {
// WorkingFilters is a copy of the filters to enable apply/cancel behaviour
this.workingFilters = this.columnDef.filterValues.slice(0);
let filterItems;
let filterItems: Array<string>;
if (this.workingFilters.length === 0) {
// Filter based all available values
filterItems = this.getFilterValues(this.grid.getData(), this.columnDef);
filterItems = this.getFilterValues(this.grid.getData() as Slick.DataProvider<T>, this.columnDef);
}
else {
// Filter based on current dataView subset
filterItems = this.getAllFilterValues(this.grid.getData().getItems(), this.columnDef);
filterItems = this.getAllFilterValues((this.grid.getData() as Slick.DataProvider<T>).getItems(), this.columnDef);
}
if (!this.$menu) {
@@ -174,8 +167,8 @@ export class HeaderFilter {
this.$menu.empty();
this.addMenuItem(this.$menu, this.columnDef, 'Sort Ascending', 'sort-asc', this.options.sortAscImage);
this.addMenuItem(this.$menu, this.columnDef, 'Sort Descending', 'sort-desc', this.options.sortDescImage);
this.addMenuItem(this.$menu, this.columnDef, 'Sort Ascending', 'sort-asc');
this.addMenuItem(this.$menu, this.columnDef, 'Sort Descending', 'sort-desc');
this.addMenuInput(this.$menu, this.columnDef);
let filterOptions = '<label><input type="checkbox" value="-1" />(Select All)</label>';
@@ -209,7 +202,7 @@ export class HeaderFilter {
this.clearButton.element.id = 'filter-clear-button';
const clearElement = jQuery('#filter-clear-button');
clearElement.bind('click', (ev) => {
this.columnDef.filterValues.length = 0;
this.columnDef.filterValues!.length = 0;
this.setButtonImage($menuButton, false);
this.handleApply(ev, this.columnDef);
});
@@ -265,11 +258,11 @@ export class HeaderFilter {
this.hideMenu();
}
private changeWorkingFilter(filterItems, workingFilters, $checkbox) {
const value = $checkbox.val();
private changeWorkingFilter(filterItems: Array<string>, workingFilters: Array<string>, $checkbox: JQuery<HTMLElement>) {
const value = $checkbox.val() as number;
const $filter = $checkbox.parent().parent();
if ($checkbox.val() < 0) {
if ($checkbox.val() as number < 0) {
// Select All
if ($checkbox.prop('checked')) {
jQuery(':checkbox', $filter).prop('checked', true);
@@ -283,7 +276,7 @@ export class HeaderFilter {
if ($checkbox.prop('checked') && index < 0) {
workingFilters.push(filterItems[value]);
const nextRow = filterItems[(parseInt(value) + 1).toString()];
const nextRow = filterItems[(parseInt(<string><any>value) + 1).toString()]; // for some reason parseInt is defined as only supporting strings even though it works fine for numbers
if (nextRow && nextRow.indexOf('Error:') >= 0) {
workingFilters.push(nextRow);
}
@@ -298,7 +291,7 @@ export class HeaderFilter {
return workingFilters;
}
private setButtonImage($el, filtered) {
private setButtonImage($el: JQuery<HTMLElement>, filtered: boolean) {
const element: HTMLElement = $el.get(0);
if (filtered) {
element.className += ' filtered';
@@ -310,7 +303,7 @@ export class HeaderFilter {
}
}
private handleApply(e, columnDef) {
private handleApply(e: JQuery.Event<HTMLElement, null>, columnDef: Slick.Column<T>) {
this.hideMenu();
this.onFilterApplied.notify({ 'grid': this.grid, 'column': columnDef }, e, self);
@@ -318,10 +311,10 @@ export class HeaderFilter {
e.stopPropagation();
}
private getFilterValues(dataView, column) {
const seen: Array<any> = [];
private getFilterValues(dataView: Slick.DataProvider<T>, column: Slick.Column<T>): Array<any> {
const seen: Array<string> = [];
for (let i = 0; i < dataView.getLength(); i++) {
const value = dataView.getItem(i)[column.field];
const value = dataView.getItem(i)[column.field!];
if (!_.contains(seen, value)) {
seen.push(value);
@@ -330,10 +323,10 @@ export class HeaderFilter {
return seen;
}
private getFilterValuesByInput($input) {
private getFilterValuesByInput($input: JQuery<HTMLElement>): Array<string> {
const column = $input.data('column'),
filter = $input.val(),
dataView = this.grid.getData(),
filter = $input.val() as string,
dataView = this.grid.getData() as Slick.DataProvider<T>,
seen: Array<any> = [];
for (let i = 0; i < dataView.getLength(); i++) {
@@ -357,10 +350,10 @@ export class HeaderFilter {
return _.sortBy(seen, (v) => { return v; });
}
private getAllFilterValues(data, column) {
private getAllFilterValues(data: Array<Slick.SlickData>, column: Slick.Column<T>) {
const seen: Array<any> = [];
for (let i = 0; i < data.length; i++) {
const value = data[i][column.field];
const value = data[i][column.field!];
if (!_.contains(seen, value)) {
seen.push(value);
@@ -370,7 +363,7 @@ export class HeaderFilter {
return _.sortBy(seen, (v) => { return v; });
}
private handleMenuItemClick(e, command, columnDef) {
private handleMenuItemClick(e: JQuery.Event<HTMLElement, null>, command: string, columnDef: Slick.Column<T>) {
this.hideMenu();
this.onCommand.notify({

View File

@@ -4,44 +4,79 @@ import { escape } from 'sql/base/common/strings';
import { mixin } from 'vs/base/common/objects';
import * as nls from 'vs/nls';
export class RowDetailView {
export interface IRowDetailViewOptions<T> {
columnId?: string;
cssClass?: string;
toolTip?: string;
width?: number;
panelRows: number;
useRowClick?: boolean;
loadOnce?: boolean;
preTemplate: (item: ExtendedItem<T>) => string;
process: (item: ExtendedItem<T>) => void;
postTemplate: (item: ExtendedItem<T>) => string;
}
public onAsyncResponse = new Slick.Event<any>();
public onAsyncEndUpdate = new Slick.Event<any>();
public onAfterRowDetailToggle = new Slick.Event<any>();
public onBeforeRowDetailToggle = new Slick.Event<any>();
const defaultOptions = {
columnId: '_detail_selector',
toolTip: '',
width: 30
};
private _grid: any;
private _expandedRows: any = [];
export interface IExtendedItem<T extends Slick.SlickData> {
_collapsed?: boolean;
_parent?: IExtendedItem<T> & T;
_detailContent?: string;
_detailViewLoaded?: boolean;
_sizePadding?: number;
id?: string;
_height?: number;
_isPadding?: boolean;
name?: string;
_child?: IExtendedItem<T>;
message?: string;
}
export type ExtendedItem<T extends Slick.SlickData> = T & IExtendedItem<T>;
interface AugmentedDataView<T extends Slick.SlickData> extends Slick.Data.DataView<T> {
getItem(row: number): ExtendedItem<T>;
getItemByIdx(row: number): ExtendedItem<T>;
}
export class RowDetailView<T extends Slick.SlickData> {
public readonly onAsyncResponse = new Slick.Event<{ itemDetail: ExtendedItem<T>, detailView: string }>();
public readonly onAsyncEndUpdate = new Slick.Event<{ grid: Slick.Grid<T>, itemDetail: T }>();
public readonly onAfterRowDetailToggle = new Slick.Event<{ grid: Slick.Grid<T>, item: T }>();
public readonly onBeforeRowDetailToggle = new Slick.Event<{ grid: Slick.Grid<T>, item: T }>();
private _grid: Slick.Grid<T>;
private _expandedRows: Array<ExtendedItem<T>> = [];
private _handler = new Slick.EventHandler();
private _defaults: any = {
columnId: '_detail_selector',
cssClass: null,
toolTip: '',
width: 30
};
private _dataView: any;
private _options: any;
private _dataView: AugmentedDataView<T>;
private _options: IRowDetailViewOptions<T>;
constructor(options) {
this._options = mixin(options, this._defaults, false);
constructor(options: IRowDetailViewOptions<T>) {
this._options = options || Object.create(null);
mixin(this._options, defaultOptions, false);
}
public init(grid: any): void {
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._dataView = this._grid.getData();
this._dataView = this._grid.getData() as AugmentedDataView<T>; // this is a bad assumption but the code is written with this assumption
// Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3
this._grid.getOptions().minRowBuffer = this._options.panelRows + 3;
this._handler
.subscribe(this._grid.onClick, (e, args) => this.handleClick(e, args))
.subscribe(this._grid.onSort, (e, args) => this.handleSort(e, args))
.subscribe(this._grid.onScroll, (e, args) => this.handleScroll(e, args));
.subscribe(this._grid.onClick, (e: MouseEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(e, args))
.subscribe(this._grid.onSort, () => this.handleSort())
.subscribe(this._grid.onScroll, () => this.handleScroll());
this._grid.getData().onRowCountChanged.subscribe(() => { this._grid.updateRowCount(); this._grid.render(); });
this._grid.getData().onRowsChanged.subscribe((e, a) => { this._grid.invalidateRows(a.rows); this._grid.render(); });
this._dataView.onRowCountChanged.subscribe(() => { this._grid.updateRowCount(); this._grid.render(); });
this._dataView.onRowsChanged.subscribe((e, a) => { this._grid.invalidateRows(a.rows); this._grid.render(); });
// subscribe to the onAsyncResponse so that the plugin knows when the user server side calls finished
this.subscribeToOnAsyncResponse();
@@ -55,17 +90,17 @@ export class RowDetailView {
this.onBeforeRowDetailToggle.unsubscribe();
}
public getOptions(options: any) {
public getOptions() {
return this._options;
}
public setOptions(options: any) {
public setOptions(options: IRowDetailViewOptions<T>) {
this._options = jQuery.extend(true, {}, this._options, options);
}
public handleClick(e: any, args: any): void {
public handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void {
// clicking on a row select checkbox
if (this._options.useRowClick || this._grid.getColumns()[args.cell].id === this._options.columnId && jQuery(e.target).hasClass('detailView-toggle')) {
if (this._options.useRowClick || this._grid.getColumns()[args.cell].id === this._options.columnId && jQuery(e.target!).hasClass('detailView-toggle')) {
// if editing, try to commit
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
@@ -95,12 +130,12 @@ export class RowDetailView {
}
// Sort will just collapse all of the open items
public handleSort(e, args) {
public handleSort() {
this.collapseAll();
}
// If we scroll save detail views that go out of cache range
public handleScroll(e, args) {
public handleScroll() {
const range = this._grid.getRenderedRange();
@@ -136,10 +171,10 @@ export class RowDetailView {
}
// Toggle between showing and hiding a row
public toggleRowSelection(row) {
this._grid.getData().beginUpdate();
public toggleRowSelection(row: T) {
this._dataView.beginUpdate();
this.handleAccordionShowHide(row);
this._grid.getData().endUpdate();
this._dataView.endUpdate();
}
// Collapse all of the open items
@@ -150,7 +185,7 @@ export class RowDetailView {
}
// Saves the current state of the detail view
public saveDetailView(item) {
public saveDetailView(item: ExtendedItem<T>) {
const view = jQuery('#innerDetailView_' + item.id);
if (view) {
const html = jQuery('#innerDetailView_' + item.id).html();
@@ -161,7 +196,7 @@ export class RowDetailView {
}
// Colapse an Item so it is notlonger seen
public collapseItem(item) {
public collapseItem(item: ExtendedItem<T>) {
// Save the details on the collapse assuming onetime loading
if (this._options.loadOnce) {
@@ -169,11 +204,11 @@ export class RowDetailView {
}
item._collapsed = true;
for (let idx = 1; idx <= item._sizePadding; idx++) {
for (let idx = 1; idx <= item._sizePadding!; idx++) {
this._dataView.deleteItem(item.id + '.' + idx);
}
item._sizePadding = 0;
this._dataView.updateItem(item.id, item);
this._dataView.updateItem(item.id!, item);
// Remove the item from the expandedRows
this._expandedRows = this._expandedRows.filter((r) => {
@@ -182,7 +217,7 @@ export class RowDetailView {
}
// Expand a row given the dataview item that is to be expanded
public expandItem(item) {
public expandItem(item: ExtendedItem<T>) {
item._collapsed = false;
this._expandedRows.push(item);
@@ -197,16 +232,16 @@ export class RowDetailView {
} else {
this.onAsyncResponse.notify({
itemDetail: item,
detailView: item._detailContent
detailView: item._detailContent!
}, undefined, this);
this.applyTemplateNewLineHeight(item);
this._dataView.updateItem(item.id, item);
this._dataView.updateItem(item.id!, item);
return;
}
this.applyTemplateNewLineHeight(item);
this._dataView.updateItem(item.id, item);
this._dataView.updateItem(item.id!, item);
// async server call
this._options.process(item);
@@ -231,7 +266,7 @@ export class RowDetailView {
args.itemDetail._detailViewLoaded = true;
this._dataView.updateItem(args.itemDetail.id, args.itemDetail);
this._dataView.updateItem(args.itemDetail.id!, args.itemDetail);
// trigger an event once the post template is finished loading
this.onAsyncEndUpdate.notify({
@@ -241,7 +276,7 @@ export class RowDetailView {
});
}
public handleAccordionShowHide(item) {
public handleAccordionShowHide(item?: ExtendedItem<T>) {
if (item) {
if (!item._collapsed) {
this.collapseItem(item);
@@ -253,7 +288,7 @@ export class RowDetailView {
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
public getPaddingItem(parent, offset) {
public getPaddingItem(parent: ExtendedItem<T>, offset: number | string) {
const item: any = {};
for (const prop in this._grid.getData()) {
@@ -265,18 +300,16 @@ export class RowDetailView {
item._collapsed = true;
item._isPadding = true;
item._parent = parent;
item._offset = offset;
return item;
}
public getErrorItem(parent, offset) {
const item: any = {};
public getErrorItem(parent: ExtendedItem<T>, offset: number | string) {
const item: ExtendedItem<T> = Object.create(null);
item.id = parent.id + '.' + offset;
item._collapsed = true;
item._isPadding = false;
item._parent = parent;
item._offset = offset;
item.name = parent.message ? parent.message : nls.localize('rowDetailView.loadError', 'Loading Error...');
parent._child = item;
return item;
@@ -285,17 +318,17 @@ export class RowDetailView {
//////////////////////////////////////////////////////////////
//create the detail ctr node. this belongs to the dev & can be custom-styled as per
//////////////////////////////////////////////////////////////
public applyTemplateNewLineHeight(item, showError = false) {
public applyTemplateNewLineHeight(item: ExtendedItem<T>, showError = false) {
// the height seems to be calculated by the template row count (how many line of items does the template have)
const rowCount = this._options.panelRows;
//calculate padding requirements based on detail-content..
//ie. worst-case: create an invisible dom node now &find it's height.
const lineHeight = 13; //we know cuz we wrote the custom css innit ;)
item._sizePadding = Math.ceil(((rowCount * 2) * lineHeight) / this._grid.getOptions().rowHeight);
item._height = (item._sizePadding * this._grid.getOptions().rowHeight);
item._sizePadding = Math.ceil(((rowCount * 2) * lineHeight) / this._grid.getOptions().rowHeight!);
item._height = (item._sizePadding * this._grid.getOptions().rowHeight!);
const idxParent = this._dataView.getIdxById(item.id);
const idxParent = this._dataView.getIdxById(item.id!);
for (let idx = 1; idx <= item._sizePadding; idx++) {
if (showError) {
this._dataView.insertItem(idxParent + idx, this.getErrorItem(item, 'error'));
@@ -305,7 +338,7 @@ export class RowDetailView {
}
}
public getColumnDefinition() {
public getColumnDefinition(): Slick.Column<T> {
return {
id: this._options.columnId,
name: '',
@@ -315,11 +348,11 @@ export class RowDetailView {
resizable: false,
sortable: false,
cssClass: this._options.cssClass,
formatter: (row, cell, value, columnDef, dataContext) => this.detailSelectionFormatter(row, cell, value, columnDef, dataContext)
formatter: (row, cell, value, columnDef, dataContext) => this.detailSelectionFormatter(row, cell, value, columnDef, dataContext as ExtendedItem<T>)
};
}
public detailSelectionFormatter(row, cell, value, columnDef, dataContext) {
public detailSelectionFormatter(row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: ExtendedItem<T>): string | undefined {
if (dataContext._collapsed === undefined) {
dataContext._collapsed = true;
@@ -327,7 +360,6 @@ export class RowDetailView {
dataContext._height = 0; //the actual height in pixels of the detail field
dataContext._isPadding = false;
dataContext._parent = undefined;
dataContext._offset = 0;
}
if (dataContext._isPadding === true) {
@@ -336,7 +368,7 @@ export class RowDetailView {
return '<div class=\'detailView-toggle expand\'></div>';
} else {
const html: Array<string> = [];
const rowHeight = this._grid.getOptions().rowHeight;
const rowHeight = this._grid.getOptions().rowHeight!;
const bottomMargin = 5;
//V313HAX:
@@ -353,59 +385,12 @@ export class RowDetailView {
html.push(`<div id='cellDetailView_${dataContext.id}' class='dynamic-cell-detail' `); //apply custom css to detail
html.push(`style=\'height:${dataContext._height}px;`); //set total height of padding
html.push(`top:${rowHeight}px'>`); //shift detail below 1st row
html.push(`<div id='detailViewContainer_${dataContext.id}"' class='detail-container' style='max-height:${(dataContext._height - rowHeight + bottomMargin)}px'>`); //sub ctr for custom styling
html.push(`<div id='innerDetailView_${dataContext.id}'>${escape(dataContext._detailContent)}</div></div>`);
html.push(`<div id='detailViewContainer_${dataContext.id}"' class='detail-container' style='max-height:${(dataContext._height! - rowHeight + bottomMargin)}px'>`); //sub ctr for custom styling
html.push(`<div id='innerDetailView_${dataContext.id}'>${escape(dataContext._detailContent!)}</div></div>`);
//&omit a final closing detail container </div> that would come next
return html.join('');
}
return null;
}
public resizeDetailView(item) {
if (!item) {
return;
}
// Grad each of the dom items
const mainContainer = document.getElementById('detailViewContainer_' + item.id);
const cellItem = document.getElementById('cellDetailView_' + item.id);
const inner = document.getElementById('innerDetailView_' + item.id);
if (!mainContainer || !cellItem || !inner) {
return;
}
for (let idx = 1; idx <= item._sizePadding; idx++) {
this._dataView.deleteItem(item.id + '.' + idx);
}
const rowHeight = this._grid.getOptions().rowHeight; // height of a row
const lineHeight = 13; //we know cuz we wrote the custom css innit ;)
// Get the inner Item height as this will be the actual size
const itemHeight = inner.clientHeight;
// Now work out how many rows
const rowCount = Math.ceil(itemHeight / rowHeight) + 1;
item._sizePadding = Math.ceil(((rowCount * 2) * lineHeight) / rowHeight);
item._height = (item._sizePadding * rowHeight);
// If the padding is now more than the original minRowBuff we need to increase it
if (this._grid.getOptions().minRowBuffer < item._sizePadding) {
// Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3
this._grid.getOptions().minRowBuffer = item._sizePadding + 3;
}
mainContainer.setAttribute('style', `max-height: ${item._height}px`);
if (cellItem) {
cellItem.setAttribute('style', `height: ${item._height}px;top:${rowHeight}px`);
}
const idxParent = this._dataView.getIdxById(item.id);
for (let idx = 1; idx <= item._sizePadding; idx++) {
this._dataView.insertItem(idxParent + idx, this.getPaddingItem(item, idx));
}
return undefined;
}
}

View File

@@ -18,8 +18,8 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
public init(grid: Slick.Grid<T>) {
this.grid = grid;
this.handler
.subscribe(this.grid.onClick, (e, args) => this.handleClick(e, args))
.subscribe(this.grid.onHeaderClick, (e, args) => this.handleHeaderClick(e, args));
.subscribe(this.grid.onClick, (e: MouseEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(e, args))
.subscribe(this.grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
}
public destroy() {
@@ -56,11 +56,11 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
cssClass: this.options.cssClass,
focusable: false,
selectable: false,
formatter: (r, c, v, cd, dc) => this.formatter(r, c, v, cd, dc)
formatter: r => this.formatter(r)
};
}
private formatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
private formatter(row: number): string {
// row is zero-based, we need make it 1 based for display in the result grid
return `<span>${row + 1}</span>`;
}

View File

@@ -25,9 +25,9 @@ export class RowSelectionModel<T extends Slick.SlickData> implements Slick.Selec
public init(grid: Slick.Grid<T>) {
this._grid = grid;
this._handler
.subscribe(this._grid.onActiveCellChanged, (e, data) => this.handleActiveCellChange(e, data))
.subscribe(this._grid.onKeyDown, e => this.handleKeyDown(e))
.subscribe(this._grid.onClick, e => this.handleClick(e));
.subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, data))
.subscribe(this._grid.onKeyDown, (e: KeyboardEvent) => this.handleKeyDown(e))
.subscribe(this._grid.onClick, (e: MouseEvent) => this.handleClick(e));
}
private rangesToRows(ranges: Slick.Range[]): number[] {
@@ -116,7 +116,7 @@ export class RowSelectionModel<T extends Slick.SlickData> implements Slick.Selec
}
}
private handleClick(e: KeyboardEvent): boolean {
private handleClick(e: MouseEvent): boolean {
const cell = this._grid.getCellFromEvent(e);
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;

View File

@@ -117,6 +117,10 @@ export class TableDataView<T extends Slick.SlickData> implements IDisposableData
return this._data[index];
}
getItems(): T[] {
return this._data.slice();
}
getLengthNonFiltered(): number {
return this.filterEnabled ? this._allData.length : this._data.length;
}