table based explorer widget (#10279)

* bump sts

* extend widget container

* remove title

* wip

* refactoring

* Revert "extend widget container"

* showTitle option

* fix properties widget error

* icon column

* icon and button columns

* use textwithicon column

* icon

* refactor and filter

* context menu

* refactor

* tests

* fix hygiene

* tests

* comments
This commit is contained in:
Alan Ren
2020-05-06 13:52:20 -07:00
committed by GitHub
parent df5df38a55
commit 0ace033a6f
26 changed files with 935 additions and 520 deletions

View File

@@ -162,3 +162,27 @@
.hc-black .slick-header-menu {
color: #FFFFFF;
}
.slick-icon-cell-content,
.slick-button-cell-content {
background-position: 7px center !important;
background-repeat: no-repeat;
background-size: 16px !important;
width: 100%;
height: 100%;
padding-left: 30px;
color: inherit !important;
display: flex;
align-items: center;
}
.slick-icon-cell,
.slick-button-cell {
padding: 0px !important;
}
.slick-button-cell-content {
cursor: pointer;
border-width: 0px;
padding: 0px;
}

View File

@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TextWithIconColumnDefinition } from 'sql/base/browser/ui/table/plugins/textWithIconColumn';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Emitter } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
export interface ButtonColumnDefinition<T extends Slick.SlickData> extends TextWithIconColumnDefinition<T> {
}
export interface ButtonColumnOptions {
iconCssClass?: string;
title?: string;
width?: number;
id?: string;
}
export interface ButtonClickEventArgs<T extends Slick.SlickData> {
item: T;
position: { x: number, y: number };
}
export class ButtonColumn<T extends Slick.SlickData> implements Slick.Plugin<T> {
private _handler = new Slick.EventHandler();
private _definition: ButtonColumnDefinition<T>;
private _grid: Slick.Grid<T>;
private _onClick = new Emitter<ButtonClickEventArgs<T>>();
public onClick = this._onClick.event;
constructor(private options: ButtonColumnOptions) {
this._definition = {
id: options.id,
resizable: false,
name: '',
formatter: (row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string => {
return this.formatter(row, cell, value, columnDef, dataContext);
},
width: options.width,
selectable: false,
iconCssClassField: options.iconCssClass
};
}
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler.subscribe(grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs<T>) => this.handleClick(args));
this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs<T>) => this.handleKeyboardEvent(e as KeyboardEvent, args));
}
public destroy(): void {
this._handler.unsubscribeAll();
}
private handleClick(args: Slick.OnClickEventArgs<T>): void {
if (this.shouldFireClickEvent(args.cell)) {
this._grid.setActiveCell(args.row, args.cell);
this.fireClickEvent();
}
}
private handleKeyboardEvent(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
let event = new StandardKeyboardEvent(e);
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) && this.shouldFireClickEvent(args.cell)) {
event.stopPropagation();
event.preventDefault();
this.fireClickEvent();
}
}
public get definition(): ButtonColumnDefinition<T> {
return this._definition;
}
private fireClickEvent(): void {
const activeCell = this._grid.getActiveCell();
const activeCellPosition = this._grid.getActiveCellPosition();
if (activeCell && activeCellPosition) {
this._onClick.fire({
item: this._grid.getDataItem(activeCell.row),
position: {
x: (activeCellPosition.left + activeCellPosition.right) / 2,
y: (activeCellPosition.bottom + activeCellPosition.top) / 2
}
});
}
}
private shouldFireClickEvent(columnIndex: number): boolean {
return this._grid.getColumns()[columnIndex].id === this.definition.id;
}
private formatter(row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string {
const buttonColumn = columnDef as ButtonColumnDefinition<T>;
return `<div class="codicon icon slick-button-cell-content ${buttonColumn.iconCssClassField}" aria-label="${this.options.title}"></div>`;
}
}

View File

@@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Definition for column with icon on the left of text.
*/
export interface TextWithIconColumnDefinition<T extends Slick.SlickData> extends Slick.Column<T> {
iconCssClassField?: string;
}
export interface TextWithIconColumnOptions {
iconCssClassField?: string;
field?: string;
width?: number;
id?: string;
resizable?: boolean;
name?: string;
}
export class TextWithIconColumn<T extends Slick.SlickData> {
private _definition: TextWithIconColumnDefinition<T>;
constructor(options: TextWithIconColumnOptions) {
this._definition = {
id: options.id,
field: options.field,
resizable: options.resizable,
formatter: this.formatter,
width: options.width,
name: options.name,
iconCssClassField: options.iconCssClassField,
cssClass: 'slick-icon-cell'
};
}
private formatter(row: number, cell: number, value: any, columnDef: Slick.Column<T>, dataContext: T): string {
const iconColumn = columnDef as TextWithIconColumnDefinition<T>;
return `<div class="icon codicon slick-icon-cell-content ${dataContext[iconColumn.iconCssClassField]}">${value}</div>`;
}
public get definition(): TextWithIconColumnDefinition<T> {
return this._definition;
}
}

View File

@@ -50,6 +50,9 @@ export class Table<T extends Slick.SlickData> extends Widget implements IDisposa
private _onClick = new Emitter<ITableMouseEvent>();
public readonly onClick: Event<ITableMouseEvent> = this._onClick.event;
private _onDoubleClick = new Emitter<ITableMouseEvent>();
public readonly onDoubleClick: Event<ITableMouseEvent> = this._onDoubleClick.event;
private _onHeaderClick = new Emitter<ITableMouseEvent>();
public readonly onHeaderClick: Event<ITableMouseEvent> = this._onHeaderClick.event;
@@ -116,6 +119,7 @@ export class Table<T extends Slick.SlickData> 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.mapMouseEvent(this._grid.onDblClick, this._onDoubleClick);
this._grid.onColumnsResized.subscribe(() => this._onColumnResize.fire());
}