SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-table * {
box-sizing: border-box;
}
.monaco-table {
width: 100%;
height: 100%;
}
.monaco-table > div {
width: 100%;
height: 100%;
}
.slick-sort-indicator-asc {
background: url('sort-asc.gif');
}
.slick-sort-indicator-desc {
background: url('sort-desc.gif');
}
.slick-sort-indicator {
display: inline-block;
width: 8px;
height: 5px;
margin-left: 4px;
margin-top: 6px;
}

View File

@@ -0,0 +1,137 @@
// Adapted from https://github.com/naresh-n/slickgrid-column-data-autosize/blob/master/src/slick.autocolumnsize.js
import { mixin, clone } from 'vs/base/common/objects';
export interface IAutoColumnSizeOptions extends Slick.PluginOptions {
maxWidth?: number;
}
const defaultOptions: IAutoColumnSizeOptions = {
maxWidth: 200
};
export class AutoColumnSize<T> implements Slick.Plugin<T> {
private _grid: Slick.Grid<T>;
private _$container: JQuery;
private _context: CanvasRenderingContext2D;
private _options: IAutoColumnSizeOptions;
constructor(options: IAutoColumnSizeOptions) {
this._options = mixin(options, defaultOptions, false);
}
public init(grid: Slick.Grid<T>) {
this._grid = grid;
this._$container = $(this._grid.getContainerNode());
this._$container.on('dblclick.autosize', '.slick-resizable-handle', e => this.reSizeColumn(e));
this._context = document.createElement('canvas').getContext('2d');
}
public destroy() {
this._$container.off();
}
private reSizeColumn(e: Event) {
let headerEl = $(e.currentTarget).closest('.slick-header-column');
let columnDef = headerEl.data('column');
if (!columnDef || !columnDef.resizable) {
return;
}
e.preventDefault();
e.stopPropagation();
let headerWidth = this.getElementWidth(headerEl[0]);
let colIndex = this._grid.getColumnIndex(columnDef.id);
let origCols = this._grid.getColumns();
let allColumns = clone(origCols);
allColumns.forEach((col, index) => {
col.formatter = origCols[index].formatter;
col.asyncPostRender = origCols[index].asyncPostRender;
});
let column = allColumns[colIndex];
let autoSizeWidth = Math.max(headerWidth, this.getMaxColumnTextWidth(columnDef, colIndex)) + 1;
if (autoSizeWidth !== column.width) {
allColumns[colIndex].width = autoSizeWidth;
this._grid.setColumns(allColumns);
this._grid.onColumnsResized.notify();
}
}
private getMaxColumnTextWidth(columnDef, colIndex: number): number {
let texts = [];
let rowEl = this.createRow(columnDef);
let data = this._grid.getData();
let viewPort = this._grid.getViewport();
let start = Math.max(0, viewPort.top + 1);
let end = Math.min(data.getLength(), viewPort.bottom);
for (let i = start; i < end; i++) {
texts.push(data.getItem(i)[columnDef.field]);
}
let template = this.getMaxTextTemplate(texts, columnDef, colIndex, data, rowEl);
let width = this.getTemplateWidth(rowEl, template);
this.deleteRow(rowEl);
return width;
}
private getTemplateWidth(rowEl: JQuery, template: JQuery | HTMLElement): number {
let cell = $(rowEl.find('.slick-cell'));
cell.append(template);
$(cell).find('*').css('position', 'relative');
return cell.outerWidth() + 1;
}
private getMaxTextTemplate(texts: string[], columnDef, colIndex: number, data, rowEl: JQuery): JQuery | HTMLElement {
let max = 0,
maxTemplate = null;
let formatFun = columnDef.formatter;
texts.forEach((text, index) => {
let template;
if (formatFun) {
template = $('<span>' + formatFun(index, colIndex, text, columnDef, data[index]) + '</span>');
text = template.text() || text;
}
let length = text ? this.getElementWidthUsingCanvas(rowEl, text) : 0;
if (length > max) {
max = length;
maxTemplate = template || text;
}
});
return maxTemplate;
}
private createRow(columnDef): JQuery {
let rowEl = $('<div class="slick-row"><div class="slick-cell"></div></div>');
rowEl.find('.slick-cell').css({
'visibility': 'hidden',
'text-overflow': 'initial',
'white-space': 'nowrap'
});
let gridCanvas = this._$container.find('.grid-canvas');
$(gridCanvas).append(rowEl);
return rowEl;
}
private deleteRow(rowEl: JQuery) {
$(rowEl).remove();
}
private getElementWidth(element: HTMLElement): number {
let width, clone = element.cloneNode(true) as HTMLElement;
clone.style.cssText = 'position: absolute; visibility: hidden;right: auto;text-overflow: initial;white-space: nowrap;';
element.parentNode.insertBefore(clone, element);
width = clone.offsetWidth;
clone.parentNode.removeChild(clone);
return width;
}
private getElementWidthUsingCanvas(element: JQuery, text: string): number {
this._context.font = element.css('font-size') + ' ' + element.css('font-family');
let metrics = this._context.measureText(text);
return metrics.width;
}
}

View File

@@ -0,0 +1,189 @@
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.checkboxselectcolumn.js
import 'vs/css!vs/base/browser/ui/checkbox/checkbox';
import { mixin } from 'vs/base/common/objects';
import * as nls from 'vs/nls';
import { ICheckboxStyles } from 'vs/base/browser/ui/checkbox/checkbox';
import { Color } from 'vs/base/common/color';
import * as strings from 'vs/base/common/strings';
export interface ICheckboxSelectColumnOptions extends Slick.PluginOptions, ICheckboxStyles {
columnId?: string;
cssClass?: string;
toolTip?: string;
width?: number;
title?: string;
}
const defaultOptions: ICheckboxSelectColumnOptions = {
columnId: '_checkbox_selector',
cssClass: null,
toolTip: nls.localize('selectDeselectAll', 'Select/Deselect All'),
width: 30,
inputActiveOptionBorder: Color.fromHex('#007ACC')
};
const checkBoxTemplate = `<div style="display: flex; align-items: center; flex-direction: column">
<div style="border-color: {0}" role="checkbox" aria-checked="{1}" aria-label="" class="custom-checkbox sql-checkbox {2}"></div>
</div>`;
export class CheckboxSelectColumn<T> implements Slick.Plugin<T> {
private _options: ICheckboxSelectColumnOptions;
private _grid: Slick.Grid<T>;
private _handler = new Slick.EventHandler();
private _selectedRowsLookup = {};
private _checkboxTemplate: string;
constructor(options?: Slick.PluginOptions) {
this._options = mixin(options, defaultOptions, false);
this.applyStyles();
}
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));
}
public destroy(): void {
this._handler.unsubscribeAll();
}
private handleSelectedRowsChanged(e: Event, args: Slick.OnSelectedRowsChangedEventArgs<T>): void {
let selectedRows = this._grid.getSelectedRows();
let lookup = {}, row, i;
for (i = 0; i < selectedRows.length; i++) {
row = selectedRows[i];
lookup[row] = true;
if (lookup[row] !== this._selectedRowsLookup[row]) {
this._grid.invalidateRow(row);
delete this._selectedRowsLookup[row];
}
}
for (i in this._selectedRowsLookup) {
this._grid.invalidateRow(i);
}
this._selectedRowsLookup = lookup;
this._grid.render();
if (!this._options.title) {
if (selectedRows.length && selectedRows.length === this._grid.getDataLength()) {
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'checked'),
this._options.toolTip);
} else {
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'unchecked'),
this._options.toolTip);
}
}
}
private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs<T>): void {
if (e.which === 32) {
if (this._grid.getColumns()[args.cell].id === this._options.columnId) {
// if editing, try to commit
if (!this._grid.getEditorLock().isActive() || this._grid.getEditorLock().commitCurrentEdit()) {
this.toggleRowSelection(args.row);
}
e.preventDefault();
e.stopImmediatePropagation();
}
}
}
private handleClick(e: Event, args: Slick.OnClickEventArgs<T>): void {
// clicking on a row select checkbox
if (this._grid.getColumns()[args.cell].id === this._options.columnId && $(e.target).is('.custom-checkbox')) {
// if editing, try to commit
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
this.toggleRowSelection(args.row);
e.stopPropagation();
e.stopImmediatePropagation();
}
}
private toggleRowSelection(row: number): void {
if (this._selectedRowsLookup[row]) {
this._grid.setSelectedRows(this._grid.getSelectedRows().filter(n => n !== row));
} else {
this._grid.setSelectedRows(this._grid.getSelectedRows().concat(row));
}
}
private handleHeaderClick(e: Event, args: Slick.OnHeaderClickEventArgs<T>): void {
if (!this._options.title && args.column.id === this._options.columnId && $(e.target).is('.custom-checkbox')) {
// if editing, try to commit
if (this._grid.getEditorLock().isActive() && !this._grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
if ($(e.target).is('.unchecked')) {
let rows = [];
for (let i = 0; i < this._grid.getDataLength(); i++) {
rows.push(i);
}
this._grid.setSelectedRows(rows);
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'checked'),
this._options.toolTip);
} else {
this._grid.setSelectedRows([]);
this._grid.updateColumnHeader(this._options.columnId,
strings.format(this._checkboxTemplate, 'true', 'unchecked'), this._options.toolTip);
e.stopPropagation();
e.stopImmediatePropagation();
}
}
}
public getColumnDefinition(): Slick.Column<T> {
return {
id: this._options.columnId,
name: this._options.title || strings.format(this._checkboxTemplate, 'true', 'unchecked'),
toolTip: this._options.toolTip,
field: 'sel',
width: this._options.width,
resizable: false,
sortable: false,
cssClass: this._options.cssClass,
formatter: (r, c, v, cd, dc) => this.checkboxSelectionFormatter(r, c, v, cd, dc)
};
}
private checkboxSelectionFormatter(row, cell, value, columnDef: Slick.Column<T>, dataContext): string {
if (dataContext) {
return this._selectedRowsLookup[row]
? strings.format(this._checkboxTemplate, 'true', 'checked')
: strings.format(this._checkboxTemplate, 'true', 'unchecked');
}
return null;
}
public style(styles: ICheckboxStyles): void {
if (styles.inputActiveOptionBorder) {
this._options.inputActiveOptionBorder = styles.inputActiveOptionBorder;
}
this.applyStyles();
}
protected applyStyles(): void {
this._checkboxTemplate = strings.format(checkBoxTemplate, this._options.inputActiveOptionBorder.toString(), '{0}', '{1}');
if (this._grid) {
this._grid.invalidateAllRows();
this._grid.render();
}
}
}

View File

@@ -0,0 +1,364 @@
// Drag select selection model gist taken from https://gist.github.com/skoon/5312536
// heavily modified
import { clone } from 'vs/base/common/objects';
export class DragCellSelectionModel<T> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private readonly keyColResizeIncr = 5;
private _grid: Slick.Grid<T>;
private _ranges: Array<Slick.Range> = [];
private _dragging = false;
private _handler = new Slick.EventHandler();
public onSelectedRangesChanged = new Slick.Event<Slick.Range[]>();
public init(grid: Slick.Grid<T>): void {
this._grid = grid;
this._handler.subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) => this.handleActiveCellChange(e, data));
this._handler.subscribe(this._grid.onKeyDown, (e: JQueryInputEventObject) => this.handleKeyDown(e));
this._handler.subscribe(this._grid.onClick, (e: MouseEvent) => this.handleClick(e));
this._handler.subscribe(this._grid.onDrag, (e: MouseEvent) => this.handleDrag(e));
this._handler.subscribe(this._grid.onDragInit, (e: MouseEvent) => this.handleDragInit(e));
this._handler.subscribe(this._grid.onDragStart, (e: MouseEvent) => this.handleDragStart(e));
this._handler.subscribe(this._grid.onDragEnd, (e: MouseEvent) => this.handleDragEnd(e));
this._handler.subscribe(this._grid.onHeaderClick, (e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) => this.handleHeaderClick(e, args));
}
public destroy(): void {
this._handler.unsubscribeAll();
}
private rangesToRows(ranges: Array<Slick.Range>): Array<number> {
let rows = [];
for (let i = 0; i < ranges.length; i++) {
for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
rows.push(j);
}
}
return rows;
}
private rowsToRanges(rows: Array<number>): Array<Slick.Range> {
let ranges = [];
let lastCell = this._grid.getColumns().length - 1;
for (let i = 0; i < rows.length; i++) {
ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
}
return ranges;
}
public getSelectedRows(): Array<number> {
return this.rangesToRows(this._ranges);
}
public setSelectedRows(rows: Array<number>) {
this.setSelectedRanges(this.rowsToRanges(rows));
}
public setSelectedRanges(ranges: Array<Slick.Range>) {
this._ranges = ranges;
this.onSelectedRangesChanged.notify(this._ranges);
}
public getSelectedRanges(): Array<Slick.Range> {
return this._ranges;
}
private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs<T>) { }
private isNavigationKey(e: BaseJQueryEventObject) {
// Nave keys (home, end, arrows) are all in sequential order so use a
switch (e.which) {
case $.ui.keyCode.HOME:
case $.ui.keyCode.END:
case $.ui.keyCode.LEFT:
case $.ui.keyCode.UP:
case $.ui.keyCode.RIGHT:
case $.ui.keyCode.DOWN:
return true;
default:
return false;
}
}
private navigateLeft(e: JQueryInputEventObject, activeCell: Slick.Cell) {
if (activeCell.cell > 1) {
let isHome = e.which === $.ui.keyCode.HOME;
let newActiveCellColumn = isHome ? 1 : activeCell.cell - 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the rightmost edge of the range and we navigate left,
// we want to deselect the rightmost cell
if (last.fromCell <= newRangeColumn) { last.toCell -= 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private navigateRight(e: JQueryInputEventObject, activeCell: Slick.Cell) {
let columnLength = this._grid.getColumns().length;
if (activeCell.cell < columnLength) {
let isEnd = e.which === $.ui.keyCode.END;
let newActiveCellColumn = isEnd ? columnLength : activeCell.cell + 1;
// Unsure why but for range, must record 1 index less than expected
let newRangeColumn = newActiveCellColumn - 1;
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the leftmost edge of the range and we navigate right,
// we want to deselect the leftmost cell
if (newRangeColumn <= last.toCell) { last.fromCell += 1; }
let fromRow = Math.min(activeCell.row, last.fromRow);
let fromCell = Math.min(newRangeColumn, last.fromCell);
let toRow = Math.max(activeCell.row, last.toRow);
let toCell = Math.max(newRangeColumn, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row, newRangeColumn, activeCell.row, newRangeColumn)];
}
this._grid.setActiveCell(activeCell.row, newActiveCellColumn);
this.setSelectedRanges(this._ranges);
}
}
private handleKeyDown(e: JQueryInputEventObject) {
let activeCell = this._grid.getActiveCell();
if (activeCell) {
// navigation keys
if (this.isNavigationKey(e)) {
e.stopImmediatePropagation();
if (e.ctrlKey || e.metaKey) {
let event = new CustomEvent('gridnav', {
detail: {
which: e.which,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey,
altKey: e.altKey
}
});
window.dispatchEvent(event);
return;
}
// end key
if (e.which === $.ui.keyCode.END) {
this.navigateRight(e, activeCell);
}
// home key
if (e.which === $.ui.keyCode.HOME) {
this.navigateLeft(e, activeCell);
}
// left arrow
if (e.which === $.ui.keyCode.LEFT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width - this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateLeft(e, activeCell);
}
// up arrow
} else if (e.which === $.ui.keyCode.UP && activeCell.row > 0) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the bottommost edge of the range and we navigate up,
// we want to deselect the bottommost row
let newRangeRow = activeCell.row - 1;
if (last.fromRow <= newRangeRow) { last.toRow -= 1; }
let fromRow = Math.min(activeCell.row - 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(newRangeRow, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row - 1, activeCell.cell - 1, activeCell.row - 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row - 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
// right arrow
} else if (e.which === $.ui.keyCode.RIGHT) {
// column resize
if ((e.ctrlKey || e.metaKey) && e.shiftKey) {
let allColumns = clone(this._grid.getColumns());
allColumns[activeCell.cell - 1].width = allColumns[activeCell.cell - 1].width + this.keyColResizeIncr;
this._grid.setColumnWidths(allColumns);
} else {
this.navigateRight(e, activeCell);
}
// down arrow
} else if (e.which === $.ui.keyCode.DOWN && activeCell.row < this._grid.getDataLength() - 1) {
if (e.shiftKey) {
let last = this._ranges.pop();
// If we are on the topmost edge of the range and we navigate down,
// we want to deselect the topmost row
let newRangeRow = activeCell.row + 1;
if (newRangeRow <= last.toRow) { last.fromRow += 1; }
let fromRow = Math.min(activeCell.row + 1, last.fromRow);
let fromCell = Math.min(activeCell.cell - 1, last.fromCell);
let toRow = Math.max(activeCell.row + 1, last.toRow);
let toCell = Math.max(activeCell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(activeCell.row + 1, activeCell.cell - 1, activeCell.row + 1, activeCell.cell - 1)];
}
this._grid.setActiveCell(activeCell.row + 1, activeCell.cell);
this.setSelectedRanges(this._ranges);
}
}
}
}
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>) {
let columnIndex = this._grid.getColumnIndex(args.column.id);
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex));
this._grid.setActiveCell(0, columnIndex + 1);
} else if (e.shiftKey && this._ranges.length) {
let last = this._ranges.pop().fromCell;
let from = Math.min(columnIndex, last);
let to = Math.max(columnIndex, last);
this._ranges = [];
for (let i = from; i <= to; i++) {
if (i !== last) {
this._ranges.push(new Slick.Range(0, i, this._grid.getDataLength() - 1, i));
}
}
this._ranges.push(new Slick.Range(0, last, this._grid.getDataLength() - 1, last));
} else {
this._ranges = [new Slick.Range(0, columnIndex, this._grid.getDataLength() - 1, columnIndex)];
this._grid.resetActiveCell();
}
this.setSelectedRanges(this._ranges);
e.stopImmediatePropagation();
return true;
}
private handleClick(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
if (!e.ctrlKey && !e.shiftKey && !e.metaKey) {
if (cell.cell !== 0) {
this._ranges = [new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, cell.cell);
return true;
} else {
this._ranges = [new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1)];
this.setSelectedRanges(this._ranges);
this._grid.setActiveCell(cell.row, 1);
return true;
}
}
else if (this._grid.getOptions().multiSelect) {
if (e.ctrlKey || e.metaKey) {
if (cell.cell === 0) {
this._ranges.push(new Slick.Range(cell.row, 0, cell.row, this._grid.getColumns().length - 1));
this._grid.setActiveCell(cell.row, 1);
} else {
this._ranges.push(new Slick.Range(cell.row, cell.cell - 1, cell.row, cell.cell - 1));
this._grid.setActiveCell(cell.row, cell.cell);
}
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
if (cell.cell === 0) {
let fromRow = Math.min(cell.row, last.fromRow);
let toRow = Math.max(cell.row, last.fromRow);
this._ranges = [new Slick.Range(fromRow, 0, toRow, this._grid.getColumns().length - 1)];
} else {
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
}
}
}
this.setSelectedRanges(this._ranges);
return true;
}
private handleDragInit(e: MouseEvent) {
e.stopImmediatePropagation();
}
private handleDragStart(e: MouseEvent) {
let cell = this._grid.getCellFromEvent(e);
e.stopImmediatePropagation();
this._dragging = true;
if (e.ctrlKey || e.metaKey) {
this._ranges.push(new Slick.Range(cell.row, cell.cell));
this._grid.setActiveCell(cell.row, cell.cell);
} else if (this._ranges.length && e.shiftKey) {
let last = this._ranges.pop();
let fromRow = Math.min(cell.row, last.fromRow);
let fromCell = Math.min(cell.cell - 1, last.fromCell);
let toRow = Math.max(cell.row, last.toRow);
let toCell = Math.max(cell.cell - 1, last.toCell);
this._ranges = [new Slick.Range(fromRow, fromCell, toRow, toCell)];
} else {
this._ranges = [new Slick.Range(cell.row, cell.cell)];
this._grid.setActiveCell(cell.row, cell.cell);
}
this.setSelectedRanges(this._ranges);
}
private handleDrag(e: MouseEvent) {
if (this._dragging) {
let cell = this._grid.getCellFromEvent(e);
let activeCell = this._grid.getActiveCell();
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
this._ranges.pop();
if (activeCell.cell === 0) {
let lastCell = this._grid.getColumns().length - 1;
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
this._ranges.push(new Slick.Range(firstRow, 0, lastRow, lastCell));
} else {
let firstRow = Math.min(cell.row, activeCell.row);
let lastRow = Math.max(cell.row, activeCell.row);
let firstColumn = Math.min(cell.cell - 1, activeCell.cell - 1);
let lastColumn = Math.max(cell.cell - 1, activeCell.cell - 1);
this._ranges.push(new Slick.Range(firstRow, firstColumn, lastRow, lastColumn));
}
this.setSelectedRanges(this._ranges);
return true;
}
return false;
}
private handleDragEnd(e: MouseEvent) {
this._dragging = false;
}
}

View File

@@ -0,0 +1,164 @@
// Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowselectionmodel.js
// heavily modified
import { mixin } from 'vs/base/common/objects';
const defaultOptions: IRowSelectionModelOptions = {
selectActiveRow: true
};
export interface IRowSelectionModelOptions extends Slick.PluginOptions {
selectActiveRow?: boolean;
}
export class RowSelectionModel<T extends Slick.SlickData> implements Slick.SelectionModel<T, Array<Slick.Range>> {
private _options: IRowSelectionModelOptions;
private _grid: Slick.Grid<T>;
private _handler = new Slick.EventHandler();
private _ranges: Array<Slick.Range> = [];
public onSelectedRangesChanged = new Slick.Event<Array<Slick.Range>>();
constructor(options?: Slick.PluginOptions) {
this._options = mixin(options, defaultOptions, false);
}
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));
}
private rangesToRows(ranges: Slick.Range[]): number[] {
let rows = [];
for (let i = 0; i < ranges.length; i++) {
for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
rows.push(j);
}
}
return rows;
}
private rowsToRanges(rows: number[]): Slick.Range[] {
let ranges = [];
let lastCell = this._grid.getColumns().length - 1;
for (let i = 0; i < rows.length; i++) {
ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
}
return ranges;
}
public getSelectedRows(): number[] {
return this.rangesToRows(this._ranges);
}
public setSelectedRows(rows: number[]): void {
this.setSelectedRanges(this.rowsToRanges(rows));
}
public setSelectedRanges(ranges: Slick.Range[]): void {
// simle check for: empty selection didn't change, prevent firing onSelectedRangesChanged
if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }
this._ranges = ranges;
this.onSelectedRangesChanged.notify(this._ranges);
}
public getSelectedRanges(): Slick.Range[] {
return this._ranges;
}
private getRowsRange(from: number, to: number): number[] {
let i, rows = [];
for (i = from; i <= to; i++) {
rows.push(i);
}
for (i = to; i < from; i++) {
rows.push(i);
}
return rows;
}
private handleActiveCellChange(e: Event, data: Slick.OnActiveCellChangedEventArgs<T>): void {
if (this._options.selectActiveRow && data.row !== null) {
this.setSelectedRanges([new Slick.Range(data.row, 0, data.row, this._grid.getColumns().length - 1)]);
}
}
private handleKeyDown(e: KeyboardEvent): void {
let activeRow = this._grid.getActiveCell();
if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which === 38 || e.which === 40)) {
let selectedRows = this.getSelectedRows();
selectedRows.sort((x, y) => x - y);
if (!selectedRows.length) {
selectedRows = [activeRow.row];
}
let top = selectedRows[0];
let bottom = selectedRows[selectedRows.length - 1];
let active;
if (e.which === 40) {
active = activeRow.row < bottom || top === bottom ? ++bottom : ++top;
} else {
active = activeRow.row < bottom ? --bottom : --top;
}
if (active >= 0 && active < this._grid.getDataLength()) {
this._grid.scrollRowIntoView(active, undefined);
let tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom));
this.setSelectedRanges(tempRanges);
}
e.preventDefault();
e.stopPropagation();
}
}
private handleClick(e: KeyboardEvent): boolean {
let cell = this._grid.getCellFromEvent(e);
if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {
return false;
}
if (!this._grid.getOptions().multiSelect || (
!e.ctrlKey && !e.shiftKey && !e.metaKey)) {
return false;
}
let selection = this.rangesToRows(this._ranges);
let idx = $.inArray(cell.row, selection);
if (idx === -1 && (e.ctrlKey || e.metaKey)) {
selection.push(cell.row);
this._grid.setActiveCell(cell.row, cell.cell);
} else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {
selection = selection.filter(o => o !== cell.row);
this._grid.setActiveCell(cell.row, cell.cell);
} else if (selection.length && e.shiftKey) {
let last = selection.pop();
let from = Math.min(cell.row, last);
let to = Math.max(cell.row, last);
selection = [];
for (let i = from; i <= to; i++) {
if (i !== last) {
selection.push(i);
}
}
selection.push(last);
this._grid.setActiveCell(cell.row, cell.cell);
}
let tempRanges = this.rowsToRanges(selection);
this.setSelectedRanges(tempRanges);
e.stopImmediatePropagation();
return true;
}
public destroy() {
this._handler.unsubscribeAll();
}
}

View File

@@ -0,0 +1,273 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/table';
import { TableDataView } from './tableDataView';
import { IThemable } from 'vs/platform/theme/common/styler';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import * as DOM from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Dimension } from 'vs/base/browser/builder';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
export interface ITableStyles extends IListStyles {
tableHeaderBackground?: Color;
tableHeaderForeground?: Color;
}
function getDefaultOptions<T>(): Slick.GridOptions<T> {
return <Slick.GridOptions<T>>{
syncColumnCellResize: true,
enableColumnReorder: false
};
}
export class Table<T extends Slick.SlickData> implements IThemable {
private _grid: Slick.Grid<T>;
private _columns: Slick.Column<T>[];
private _data: TableDataView<T>;
private _styleElement: HTMLStyleElement;
private _idPrefix: string;
private _autoscroll: boolean;
private _onRowCountChangeListener: IDisposable;
private _container: HTMLElement;
private _tableContainer: HTMLElement;
constructor(parent: HTMLElement, data?: Array<T> | TableDataView<T>, columns?: Slick.Column<T>[], options?: Slick.GridOptions<T>) {
if (data instanceof TableDataView) {
this._data = data;
} else {
this._data = new TableDataView<T>(data);
}
if (columns) {
this._columns = columns;
} else {
this._columns = new Array<Slick.Column<T>>();
}
let newOptions = mixin(options || {}, getDefaultOptions<T>(), false);
this._container = document.createElement('div');
this._container.className = 'monaco-table';
parent.appendChild(this._container);
this._styleElement = DOM.createStyleSheet(this._container);
this._tableContainer = document.createElement('div');
this._container.appendChild(this._tableContainer);
this._styleElement = DOM.createStyleSheet(this._container);
this._grid = new Slick.Grid<T>(this._tableContainer, this._data, this._columns, newOptions);
this._idPrefix = this._tableContainer.classList[0];
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
this._grid.onSort.subscribe((e, args) => {
this._data.sort(args);
this._grid.invalidate();
this._grid.render();
});
}
private _handleRowCountChange() {
this._grid.updateRowCount();
this._grid.render();
if (this._autoscroll) {
this._grid.scrollRowIntoView(this._data.getLength() - 1, false);
}
}
set columns(columns: Slick.Column<T>[]) {
this._grid.setColumns(columns);
}
setData(data: Array<T>);
setData(data: TableDataView<T>);
setData(data: Array<T> | TableDataView<T>) {
if (data instanceof TableDataView) {
this._data = data;
} else {
this._data = new TableDataView<T>(data);
}
this._onRowCountChangeListener.dispose();
this._grid.setData(this._data, true);
this._onRowCountChangeListener = this._data.onRowCountChange(() => this._handleRowCountChange());
}
get columns(): Slick.Column<T>[] {
return this._grid.getColumns();
}
setSelectedRows(rows: number[]) {
this._grid.setSelectedRows(rows);
}
getSelectedRows(): number[] {
return this._grid.getSelectedRows();
}
onSelectedRowsChanged(fn: (e: Slick.EventData, data: Slick.OnSelectedRowsChangedEventArgs<T>) => any): IDisposable
onSelectedRowsChanged(fn: (e: DOMEvent, data: Slick.OnSelectedRowsChangedEventArgs<T>) => any): IDisposable
onSelectedRowsChanged(fn: any): IDisposable {
this._grid.onSelectedRowsChanged.subscribe(fn);
return {
dispose() {
this._grid.onSelectedRowsChanged.unsubscribe(fn);
}
};
}
onContextMenu(fn: (e: Slick.EventData, data: Slick.OnContextMenuEventArgs<T>) => any): IDisposable;
onContextMenu(fn: (e: DOMEvent, data: Slick.OnContextMenuEventArgs<T>) => any): IDisposable;
onContextMenu(fn: any): IDisposable {
this._grid.onContextMenu.subscribe(fn);
return {
dispose() {
this._grid.onContextMenu.unsubscribe(fn);
}
};
}
getCellFromEvent(e: DOMEvent): Slick.Cell {
return this._grid.getCellFromEvent(e);
}
setSelectionModel(model: Slick.SelectionModel<T, Array<Slick.Range>>) {
this._grid.setSelectionModel(model);
}
focus(): void {
this._grid.focus();
}
setActiveCell(row: number, cell: number): void {
this._grid.setActiveCell(row, cell);
}
get activeCell(): Slick.Cell {
return this._grid.getActiveCell();
}
registerPlugin(plugin: Slick.Plugin<T>): void {
this._grid.registerPlugin(plugin);
}
/**
* This function needs to be called if the table is drawn off dom.
*/
resizeCanvas() {
this._grid.resizeCanvas();
}
layout(dimension: Dimension): void
layout(size: number, orientation: Orientation): void
layout(sizing: number | Dimension, orientation?: Orientation): void {
if (sizing instanceof Dimension) {
this._container.style.width = sizing.width + 'px';
this._container.style.height = sizing.height + 'px';
this._tableContainer.style.width = sizing.width + 'px';
this._tableContainer.style.height = sizing.height + 'px';
} else {
if (orientation === Orientation.HORIZONTAL) {
this._container.style.width = '100%';
this._container.style.height = sizing + 'px';
this._tableContainer.style.width = '100%';
this._tableContainer.style.height = sizing + 'px';
} else {
this._container.style.width = sizing + 'px';
this._container.style.height = '100%';
this._tableContainer.style.width = sizing + 'px';
this._tableContainer.style.height = '100%';
}
}
this.resizeCanvas();
}
autosizeColumns() {
this._grid.autosizeColumns();
}
set autoScroll(active: boolean) {
this._autoscroll = active;
}
style(styles: ITableStyles): void {
const content: string[] = [];
if (styles.tableHeaderBackground) {
content.push(`.monaco-table .${this._idPrefix} .slick-header .slick-header-column { background-color: ${styles.tableHeaderBackground}; }`);
}
if (styles.tableHeaderForeground) {
content.push(`.monaco-table .${this._idPrefix} .slick-header .slick-header-column { color: ${styles.tableHeaderForeground}; }`);
}
if (styles.listFocusBackground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .focused { background-color: ${styles.listFocusBackground}; }`);
}
if (styles.listFocusForeground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .focused { color: ${styles.listFocusForeground}; }`);
}
if (styles.listActiveSelectionBackground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { background-color: ${styles.listActiveSelectionBackground}; }`);
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected:hover { background-color: ${styles.listActiveSelectionBackground}; }`); // overwrite :hover style in this case!
}
if (styles.listActiveSelectionForeground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { color: ${styles.listActiveSelectionForeground}; }`);
}
if (styles.listFocusAndSelectionBackground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { background-color: ${styles.listFocusAndSelectionBackground}; }`);
}
if (styles.listFocusAndSelectionForeground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected.active { color: ${styles.listFocusAndSelectionForeground}; }`);
}
/* Commented out andresse 8/17/2017; keeping for reference as we iterate on the table styling */
// if (styles.listInactiveFocusBackground) {
// content.push(`.monaco-table .${this._idPrefix} .slick-row.focused { background-color: ${styles.listInactiveFocusBackground}; }`);
// content.push(`.monaco-table .${this._idPrefix} .slick-row.focused:hover { background-color: ${styles.listInactiveFocusBackground}; }`); // overwrite :hover style in this case!
// }
// if (styles.listInactiveSelectionBackground) {
// content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { background-color: ${styles.listInactiveSelectionBackground}; }`);
// content.push(`.monaco-table .${this._idPrefix} .slick-row .selected:hover { background-color: ${styles.listInactiveSelectionBackground}; }`); // overwrite :hover style in this case!
// }
// if (styles.listInactiveSelectionForeground) {
// content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { color: ${styles.listInactiveSelectionForeground}; }`);
// }
if (styles.listHoverBackground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { background-color: ${styles.listHoverBackground}; }`);
}
if (styles.listHoverForeground) {
content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { color: ${styles.listHoverForeground}; }`);
}
if (styles.listSelectionOutline) {
content.push(`.monaco-table .${this._idPrefix} .slick-row .selected { outline: 1px dotted ${styles.listSelectionOutline}; outline-offset: -1px; }`);
}
/* Commented out andresse 8/17/2017; keeping for reference as we iterate on the table styling */
// if (styles.listFocusOutline) {
// content.push(`.monaco-table .${this._idPrefix}:focus .slick-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`);
// }
// if (styles.listInactiveFocusOutline) {
// content.push(`.monaco-table .${this._idPrefix} .slick-row.focused { outline: 1px dotted ${styles.listInactiveFocusOutline}; outline-offset: -1px; }`);
// }
if (styles.listHoverOutline) {
content.push(`.monaco-table .${this._idPrefix} .slick-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
}
this._styleElement.innerHTML = content.join('\n');
}
}

View File

@@ -0,0 +1,157 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import Event, { Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import * as types from 'vs/base/common/types';
export interface IFindPosition {
col: number;
row: number;
}
function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T> {
let field = args.sortCol.field;
let sign = args.sortAsc ? 1 : -1;
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);
}
export class TableDataView<T extends Slick.SlickData> implements Slick.DataProvider<T> {
private _data: Array<T>;
private _findArray: Array<IFindPosition>;
private _findObs: Observable<IFindPosition>;
private _findIndex: number;
private _onRowCountChange = new Emitter<number>();
get onRowCountChange(): Event<number> { return this._onRowCountChange.event; }
private _onFindCountChange = new Emitter<number>();
get onFindCountChange(): Event<number> { return this._onFindCountChange.event; }
constructor(
data?: Array<T>,
private _findFn?: (val: T, exp: string) => Array<number>,
private _sortFn?: (args: Slick.OnSortEventArgs<T>, data: Array<T>) => Array<T>
) {
if (data) {
this._data = data;
} else {
this._data = new Array<T>();
}
if (!_sortFn) {
this._sortFn = defaultSort;
}
}
sort(args: Slick.OnSortEventArgs<T>) {
this._data = this._sortFn(args, this._data);
}
getLength(): number {
return this._data.length;
}
getItem(index: number): T {
return this._data[index];
}
push(items: Array<T>);
push(item: T);
push(input: T | Array<T>) {
if (Array.isArray(input)) {
this._data.push(...input);
} else {
this._data.push(input);
}
this._onRowCountChange.fire();
}
clear() {
this._data = new Array<T>();
this._onRowCountChange.fire();
}
find(exp: string): Thenable<IFindPosition> {
if (!this._findFn) {
return TPromise.wrapError(new Error('no find function provided'));
}
this._findArray = new Array<IFindPosition>();
this._findIndex = 0;
this._onFindCountChange.fire(this._findArray.length);
if (exp) {
this._findObs = Observable.create((observer: Observer<IFindPosition>) => {
this._data.forEach((item, i) => {
let result = this._findFn(item, exp);
if (result) {
result.forEach(pos => {
let index = { col: pos, row: i };
this._findArray.push(index);
observer.next(index);
this._onFindCountChange.fire(this._findArray.length);
});
}
});
});
return this._findObs.take(1).toPromise().then(() => {
return this._findArray[this._findIndex];
});
} else {
return TPromise.wrapError(new Error('no expression'));
}
}
clearFind() {
this._findArray = new Array<IFindPosition>();
this._findIndex = 0;
this._findObs = undefined;
this._onFindCountChange.fire(this._findArray.length);
}
findNext(): Thenable<IFindPosition> {
if (this._findArray && this._findArray.length !== 0) {
if (this._findIndex === this._findArray.length - 1) {
this._findIndex = 0;
} else {
++this._findIndex;
}
return TPromise.as(this._findArray[this._findIndex]);
} else {
return TPromise.wrapError(new Error('no search running'));
}
}
findPrevious(): Thenable<IFindPosition> {
if (this._findArray && this._findArray.length !== 0) {
if (this._findIndex === 0) {
this._findIndex = this._findArray.length - 1;
} else {
--this._findIndex;
}
return TPromise.as(this._findArray[this._findIndex]);
} else {
return TPromise.wrapError(new Error('no search running'));
}
}
get currentFindPosition(): Thenable<IFindPosition> {
if (this._findArray && this._findArray.length !== 0) {
return TPromise.as(this._findArray[this._findIndex]);
} else {
return TPromise.wrapError(new Error('no search running'));
}
}
/* 1 indexed */
get findPosition(): number {
return types.isUndefinedOrNull(this._findIndex) ? 0 : this._findIndex + 1;
}
get findCount(): number {
return types.isUndefinedOrNull(this._findArray) ? 0 : this._findArray.length;
}
}

View File

@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Table } from './table';
import { TableDataView } from './tableDataView';
import { View, Orientation, AbstractCollapsibleView, HeaderView, IViewOptions, ICollapsibleViewOptions } from 'vs/base/browser/ui/splitview/splitview';
import { $ } from 'vs/base/browser/builder';
export class TableBasicView<T> extends View {
private _table: Table<T>;
private _container: HTMLElement;
constructor(
viewOpts: IViewOptions,
data?: Array<T> | TableDataView<T>,
columns?: Slick.Column<T>[],
tableOpts?: Slick.GridOptions<T>
) {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
}
public get table(): Table<T> {
return this._table;
}
render(container: HTMLElement, orientation: Orientation): void {
container.appendChild(this._container);
}
focus(): void {
this._table.focus();
}
layout(size: number, orientation: Orientation): void {
this._table.layout(size, orientation);
}
}
export class TableHeaderView<T> extends HeaderView {
private _table: Table<T>;
private _container: HTMLElement;
constructor(
private _viewTitle: string,
viewOpts: IViewOptions,
data?: Array<T> | TableDataView<T>,
columns?: Slick.Column<T>[],
tableOpts?: Slick.GridOptions<T>
) {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
}
public get table(): Table<T> {
return this._table;
}
protected renderHeader(container: HTMLElement): void {
const titleDiv = $('div.title').appendTo(container);
$('span').text(this._viewTitle).appendTo(titleDiv);
}
protected renderBody(container: HTMLElement): void {
container.appendChild(this._container);
}
protected layoutBody(size: number): void {
this._table.layout(size, Orientation.HORIZONTAL);
}
focus(): void {
this._table.focus();
}
}
export class TableCollapsibleView<T> extends AbstractCollapsibleView {
private _table: Table<T>;
private _container: HTMLElement;
constructor(
private _viewTitle: string,
viewOpts: ICollapsibleViewOptions,
data?: Array<T> | TableDataView<T>,
columns?: Slick.Column<T>[],
tableOpts?: Slick.GridOptions<T>
) {
super(undefined, viewOpts);
this._container = document.createElement('div');
this._container.className = 'table-view';
this._table = new Table<T>(this._container, data, columns, tableOpts);
}
public addContainerClass(className: string) {
this._container.classList.add(className);
}
public get table(): Table<T> {
return this._table;
}
protected renderHeader(container: HTMLElement): void {
const titleDiv = $('div.title').appendTo(container);
$('span').text(this._viewTitle).appendTo(titleDiv);
}
protected renderBody(container: HTMLElement): void {
container.appendChild(this._container);
}
protected layoutBody(size: number): void {
this._table.layout(size, Orientation.HORIZONTAL);
}
}