From 954d0d954fb58d7901e67bb0230e87514f8fb7ff Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Tue, 8 Jan 2019 13:05:53 -0800 Subject: [PATCH] Auto Column Sizing (#2778) * add auto column sizing * add break for performance * update with new library --- .../table/plugins/autoSizeColumns.plugin.ts | 77 ++++++++++++++++++- src/sql/parts/query/editor/gridPanel.ts | 6 +- .../query/editor/resultsGridContribution.ts | 6 +- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts b/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts index 87539edf68..e03ef056c1 100644 --- a/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/autoSizeColumns.plugin.ts @@ -1,13 +1,16 @@ // Adapted from https://github.com/naresh-n/slickgrid-column-data-autosize/blob/master/src/slick.autocolumnsize.js import { mixin, clone } from 'sql/base/common/objects'; +import { isInDOM } from 'vs/base/browser/dom'; export interface IAutoColumnSizeOptions extends Slick.PluginOptions { maxWidth?: number; + autoSizeOnRender?: boolean; } const defaultOptions: IAutoColumnSizeOptions = { - maxWidth: 200 + maxWidth: 200, + autoSizeOnRender: false }; export class AutoColumnSize implements Slick.Plugin { @@ -15,6 +18,7 @@ export class AutoColumnSize implements Slick.Plugin { private _$container: JQuery; private _context: CanvasRenderingContext2D; private _options: IAutoColumnSizeOptions; + private onPostEventHandler = new Slick.EventHandler(); constructor(options: IAutoColumnSizeOptions = defaultOptions) { this._options = mixin(options, defaultOptions, false); @@ -23,8 +27,12 @@ export class AutoColumnSize implements Slick.Plugin { public init(grid: Slick.Grid) { this._grid = grid; + if (this._options.autoSizeOnRender) { + this.onPostEventHandler.subscribe(this._grid.onRendered, () => this.onPostRender()); + } + this._$container = $(this._grid.getContainerNode()); - this._$container.on('dblclick.autosize', '.slick-resizable-handle', e => this.reSizeColumn(e)); + this._$container.on('dblclick.autosize', '.slick-resizable-handle', e => this.handleDoubleClick(e)); this._context = document.createElement('canvas').getContext('2d'); } @@ -32,7 +40,66 @@ export class AutoColumnSize implements Slick.Plugin { this._$container.off(); } - private reSizeColumn(e: JQuery.Event) { + private onPostRender() { + // this doesn't do anything if the grid isn't on the dom + if (!isInDOM(this._grid.getContainerNode())) { + return; + } + + // 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 item = data.getItem(0); + if (item && Object.keys(item).length > 0) { + let hasValue = false; + for (let key in item) { + if (item.hasOwnProperty(key)) { + if (item[key]) { + hasValue = true; + break; + } + } + } + if (!hasValue) { + return; + } + } else { + return; + } + + let headerColumnsQuery = $(this._grid.getContainerNode()).find('.slick-header-columns'); + if (headerColumnsQuery && headerColumnsQuery.length) { + let headerColumns = headerColumnsQuery[0]; + let origCols = this._grid.getColumns(); + let allColumns = clone(origCols); + allColumns.forEach((col, index) => { + col.formatter = origCols[index].formatter; + col.asyncPostRender = origCols[index].asyncPostRender; + }); + let change = false; + for (let i = 0; i <= headerColumns.children.length; i++) { + let headerEl = $(headerColumns.children.item(i)); + let columnDef = headerEl.data('column'); + if (columnDef) { + let headerWidth = this.getElementWidth(headerEl[0]); + let colIndex = this._grid.getColumnIndex(columnDef.id); + let column = allColumns[colIndex]; + let autoSizeWidth = Math.max(headerWidth, this.getMaxColumnTextWidth(columnDef, colIndex)) + 1; + if (autoSizeWidth !== column.width) { + allColumns[colIndex].width = autoSizeWidth; + change = true; + } + } + } + if (change) { + this.onPostEventHandler.unsubscribeAll(); + this._grid.setColumns(allColumns); + this._grid.onColumnsResized.notify(); + } + } + } + + private handleDoubleClick(e: JQuery.Event) { let headerEl = $(e.currentTarget).closest('.slick-header-column'); let columnDef = headerEl.data('column'); @@ -43,6 +110,10 @@ export class AutoColumnSize implements Slick.Plugin { e.preventDefault(); e.stopPropagation(); + this.reSizeColumn(headerEl, columnDef); + } + + private reSizeColumn(headerEl: JQuery, columnDef: Slick.Column) { let headerWidth = this.getElementWidth(headerEl[0]); let colIndex = this._grid.getColumnIndex(columnDef.id); let origCols = this._grid.getColumns(); diff --git a/src/sql/parts/query/editor/gridPanel.ts b/src/sql/parts/query/editor/gridPanel.ts index 56841f1cca..6b226bbc98 100644 --- a/src/sql/parts/query/editor/gridPanel.ts +++ b/src/sql/parts/query/editor/gridPanel.ts @@ -420,12 +420,12 @@ class GridTable extends Disposable implements IView { @IContextMenuService private contextMenuService: IContextMenuService, @IInstantiationService private instantiationService: IInstantiationService, @IEditorService private editorService: IEditorService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IConfigurationService private configurationService: IConfigurationService ) { super(); this.container.style.width = '100%'; this.container.style.height = '100%'; - // this.container.style.marginBottom = BOTTOM_PADDING + 'px'; this.container.className = 'grid-panel'; this.columns = this.resultSet.columnInfo.map((c, i) => { @@ -507,7 +507,7 @@ class GridTable extends Disposable implements IView { this.table = this._register(new Table(tableContainer, { dataProvider: this.dataProvider, columns: this.columns }, tableOptions)); this.table.setSelectionModel(this.selectionModel); this.table.registerPlugin(new MouseWheelSupport()); - this.table.registerPlugin(new AutoColumnSize()); + this.table.registerPlugin(new AutoColumnSize({ autoSizeOnRender: this.configurationService.getValue('resultsGrid.autoSizeColumns') })); this.table.registerPlugin(copyHandler); this.table.registerPlugin(this.rowNumberColumn); this.table.registerPlugin(new AdditionalKeyBindings()); diff --git a/src/sql/parts/query/editor/resultsGridContribution.ts b/src/sql/parts/query/editor/resultsGridContribution.ts index 70c1afd20a..5153845502 100644 --- a/src/sql/parts/query/editor/resultsGridContribution.ts +++ b/src/sql/parts/query/editor/resultsGridContribution.ts @@ -21,7 +21,6 @@ const resultsGridConfiguration: IConfigurationNode = { type: 'object', title: nls.localize('resultsGridConfigurationTitle', "Results Grid"), overridable: true, - scope: ConfigurationScope.RESOURCE, properties: { 'resultsGrid.fontFamily': { type: 'string', @@ -62,6 +61,11 @@ const resultsGridConfiguration: IConfigurationNode = { ], default: RESULTS_GRID_DEFAULTS.cellPadding, description: nls.localize('cellPadding', "Controls the cell padding in pixels") + }, + 'resultsGrid.autoSizeColumns': { + type: 'boolean', + default: false, + description: nls.localize('autoSizeColumns', "Auto size the columns width on inital results. Could have performance problems with large number of columns or large cells") } } };