From 9c5b9364b619c148b2542ac9ab907eb90247d4f8 Mon Sep 17 00:00:00 2001 From: Aditya Toshniwal Date: Tue, 26 Feb 2019 13:44:16 +0000 Subject: [PATCH] Support double-click on Query Tool result grid column resize handles to auto-size to the content. Fixes #4034 --- docs/en_US/release_notes_4_3.rst | 1 + .../js/selection/active_cell_capture.js | 5 + .../slickgrid/plugins/slick.autocolumnsize.js | 160 ++++++++++++++++++ .../tools/sqleditor/static/js/sqleditor.js | 4 +- web/webpack.shim.js | 1 + 5 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js diff --git a/docs/en_US/release_notes_4_3.rst b/docs/en_US/release_notes_4_3.rst index 9d6724b6f..ed0973330 100644 --- a/docs/en_US/release_notes_4_3.rst +++ b/docs/en_US/release_notes_4_3.rst @@ -16,6 +16,7 @@ Features | `Feature #3559 `_ - Automatically expand child nodes as well as the selected node on the treeview if there is only one. | `Feature #3886 `_ - Include multiple versions of the PG utilties in containers. | `Feature #3991 `_ - Update Alpine Linux version in the docker container. +| `Feature #4034 `_ - Support double-click on Query Tool result grid column resize handles to auto-size to the content. Bug fixes ********* diff --git a/web/pgadmin/static/js/selection/active_cell_capture.js b/web/pgadmin/static/js/selection/active_cell_capture.js index 03d7b40e5..8592992e8 100644 --- a/web/pgadmin/static/js/selection/active_cell_capture.js +++ b/web/pgadmin/static/js/selection/active_cell_capture.js @@ -62,6 +62,11 @@ define([ return; } + /* Skip if clicked on resize handler */ + if($(event.target).hasClass('slick-resizable-handle')) { + return; + } + bypassDefaultActiveCellRangeChange = true; var clickedColumn = args.column.pos + 1; diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js b/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js new file mode 100644 index 000000000..40e537e1b --- /dev/null +++ b/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js @@ -0,0 +1,160 @@ +/* + * https://github.com/naresh-n/slickgrid-column-data-autosize + */ + +(function($) { + + $.extend(true, window, { + 'Slick': { + 'AutoColumnSize': AutoColumnSize, + }, + }); + + function AutoColumnSize(maxWidth) { + + var grid, $container, context, + keyCodes = { + 'A': 65, + }; + + function init(_grid) { + grid = _grid; + maxWidth = maxWidth || 200; + + $container = $(grid.getContainerNode()); + $container.on('dblclick.autosize', '.slick-resizable-handle', reSizeColumn); + $container.keydown(handleControlKeys); + + context = document.createElement('canvas').getContext('2d'); + } + + function destroy() { + $container.off(); + } + + function handleControlKeys(event) { + if (event.ctrlKey && event.shiftKey && event.keyCode === keyCodes.A) { + resizeAllColumns(); + } + } + + function resizeAllColumns() { + var elHeaders = $container.find('.slick-header-column'); + var allColumns = grid.getColumns(); + elHeaders.each(function(index, el) { + var columnDef = $(el).data('column'); + var headerWidth = getElementWidth(el); + var colIndex = grid.getColumnIndex(columnDef.id); + var column = allColumns[colIndex]; + var autoSizeWidth = Math.max(headerWidth, getMaxColumnTextWidth(columnDef, colIndex)) + 1; + autoSizeWidth = Math.min(maxWidth, autoSizeWidth); + column.width = autoSizeWidth; + }); + grid.setColumns(allColumns); + grid.onColumnsResized.notify(); + } + + function reSizeColumn(e) { + var headerEl = $(e.currentTarget).closest('.slick-header-column'); + var columnDef = headerEl.data('column'); + + if (!columnDef || !columnDef.resizable) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + var headerWidth = getElementWidth(headerEl[0]); + var colIndex = grid.getColumnIndex(columnDef.id); + var allColumns = grid.getColumns(); + var column = allColumns[colIndex]; + + var autoSizeWidth = Math.max(headerWidth, getMaxColumnTextWidth(columnDef, colIndex)) + 1; + + if (autoSizeWidth !== column.width) { + column.width = autoSizeWidth; + grid.setColumns(allColumns); + grid.onColumnsResized.notify(); + } + } + + function getMaxColumnTextWidth(columnDef, colIndex) { + var texts = []; + var rowEl = createRow(); + var data = grid.getData(); + if (window.Slick.Data && data instanceof window.Slick.Data.DataView) { + data = data.getItems(); + } + for (var i = 0; i < data.length; i++) { + texts.push(data[i][columnDef.field]); + } + var template = getMaxTextTemplate(texts, columnDef, colIndex, data, rowEl); + var width = getTemplateWidth(rowEl, template); + deleteRow(rowEl); + return width; + } + + function getTemplateWidth(rowEl, template) { + var cell = $(rowEl.find('.slick-cell')); + cell.append(template); + $(cell).find('*').css('position', 'relative'); + return cell.outerWidth() + 1; + } + + function getMaxTextTemplate(texts, columnDef, colIndex, data, rowEl) { + var max = 0, + maxTemplate = null; + var formatFun = columnDef.formatter; + $(texts).each(function(index, text) { + var template; + if (formatFun) { + template = $('' + formatFun(index, colIndex, text, columnDef, data[index]) + ''); + text = template.text() || text; + } + var length = text ? getElementWidthUsingCanvas(rowEl, text) : 0; + if (length > max) { + max = length; + maxTemplate = template || text; + } + }); + return maxTemplate; + } + + function createRow() { + var rowEl = $('
'); + rowEl.find('.slick-cell').css({ + 'visibility': 'hidden', + 'text-overflow': 'initial', + 'white-space': 'nowrap', + }); + var gridCanvas = $container.find('.grid-canvas'); + $(gridCanvas).append(rowEl); + return rowEl; + } + + function deleteRow(rowEl) { + $(rowEl).remove(); + } + + function getElementWidth(element) { + var width, clone = element.cloneNode(true); + 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; + } + + function getElementWidthUsingCanvas(element, text) { + context.font = element.css('font-size') + ' ' + element.css('font-family'); + var metrics = context.measureText(text); + return metrics.width; + } + + return { + init: init, + destroy: destroy, + }; + } +}(window.jQuery)); diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index fd2ba623e..7cba76048 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -41,6 +41,7 @@ define('tools.querytool', [ 'backgrid.sizeable.columns', 'slick.pgadmin.formatters', 'slick.pgadmin.editors', + 'slick.pgadmin.plugins/slick.autocolumnsize', 'pgadmin.browser', 'pgadmin.tools.user_management', ], function( @@ -863,6 +864,7 @@ define('tools.querytool', [ grid.registerPlugin(new ActiveCellCapture()); grid.setSelectionModel(new XCellSelectionModel()); grid.registerPlugin(gridSelector); + grid.registerPlugin(new Slick.AutoColumnSize()); var headerButtonsPlugin = new Slick.Plugins.HeaderButtons(); headerButtonsPlugin.onCommand.subscribe(function (e, args) { let command = args.command; @@ -926,7 +928,7 @@ define('tools.querytool', [ var column_size = self.handler['col_size']; column_size[self.handler['table_name']][col['id']] = col['width']; }); - }); + }.bind(grid)); gridSelector.onBeforeGridSelectAll.subscribe(function(e, args) { if (self.handler.has_more_rows) { diff --git a/web/webpack.shim.js b/web/webpack.shim.js index b9645625a..2244072fd 100644 --- a/web/webpack.shim.js +++ b/web/webpack.shim.js @@ -283,6 +283,7 @@ var webpackShimConfig = { 'pgadmin.user_management.current_user': '/user_management/current_user', 'slick.pgadmin.editors': path.join(__dirname, './pgadmin/tools/../static/js/slickgrid/editors'), 'slick.pgadmin.formatters': path.join(__dirname, './pgadmin/tools/../static/js/slickgrid/formatters'), + 'slick.pgadmin.plugins': path.join(__dirname, './pgadmin/tools/../static/js/slickgrid/plugins'), }, externals: [ 'pgadmin.user_management.current_user',