Skip to content

Commit

Permalink
feat(resizer): add single Column Resize by Content dblClick & headerMenu
Browse files Browse the repository at this point in the history
- takes the previous feature of resize (all) columns by content and make it a single column resize by content. It can be triggered by a double-click on the resize column hover over OR via a new header menu command
  • Loading branch information
ghiscoding committed May 18, 2021
1 parent 513786e commit 183f33f
Show file tree
Hide file tree
Showing 26 changed files with 439 additions and 185 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"dependencies": {
"@types/jquery": "^3.5.5",
"dequal": "^2.0.2",
"dompurify": "^2.2.7",
"dompurify": "^2.2.8",
"excel-builder-webpacker": "^1.0.6",
"flatpickr": "^4.6.9",
"font-awesome": "^4.7.0",
Expand Down Expand Up @@ -165,13 +165,13 @@
"require-dir": "^1.2.0",
"rimraf": "^3.0.2",
"run-sequence": "^2.2.1",
"sass": "^1.32.11",
"standard-version": "^9.1.1",
"sass": "^1.32.13",
"standard-version": "^9.3.0",
"ts-node": "^9.1.1",
"tslib": "^2.1.0",
"tslib": "^2.2.0",
"tslint": "~6.1.3",
"typescript": "4.0.7",
"yargs": "^16.2.0",
"yargs": "^17.0.1",
"zone.js": "~0.10.2"
}
}
}
8 changes: 5 additions & 3 deletions src/app/examples/grid-resize-by-content.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,11 @@ export class GridResizeByContentComponent implements OnInit {
autosizeColumnsByCellContentOnFirstLoad: true,
enableAutoResizeColumnsByCellContent: true,

// optional resize calculation options
resizeDefaultRatioForStringType: 0.92,
resizeFormatterPaddingWidthInPx: 8, // optional editor formatter padding for resize calculation
resizeByContentOptions: {
// optional resize calculation options
defaultRatioForStringType: 0.92,
formatterPaddingWidthInPx: 8, // optional editor formatter padding for resize calculation
},

enableExcelExport: true,
excelExportOptions: {
Expand Down
1 change: 1 addition & 0 deletions src/app/modules/angular-slickgrid/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export class Constants {
TEXT_COLLAPSE_ALL_GROUPS: 'Collapse all Groups',
TEXT_CONTAINS: 'Contains',
TEXT_COLUMNS: 'Columns',
TEXT_COLUMN_RESIZE_BY_CONTENT: 'Resize by Content',
TEXT_COMMANDS: 'Commands',
TEXT_COPY: 'Copy',
TEXT_EQUALS: 'Equals',
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
SlickEventHandler,
} from '../models/index';
import { FilterService } from '../services/filter.service';
import { ResizerService } from '../services/resizer.service';
import { SortService } from '../services/sort.service';
import { SharedService } from '../services/shared.service';
import { arrayRemoveItemByIndex, getTranslationPrefix } from '../services/utilities';
Expand All @@ -34,6 +35,7 @@ export class HeaderMenuExtension implements Extension {
constructor(
private readonly extensionUtility: ExtensionUtility,
private readonly filterService: FilterService,
private readonly resizerService: ResizerService,
private readonly sharedService: SharedService,
private readonly sortService: SortService,
@Optional() private readonly translate: TranslateService,
Expand Down Expand Up @@ -136,23 +138,37 @@ export class HeaderMenuExtension implements Extension {
};
}

const columnHeaderMenuItems: Array<MenuCommandItem | 'divider'> = columnDef && columnDef.header && columnDef.header.menu && columnDef.header.menu.items || [];
const columnHeaderMenuItems: Array<MenuCommandItem | 'divider'> = columnDef?.header?.menu?.items ?? [];

// Freeze Column (pinning)
let hasFrozenOrResizeCommand = false;
if (headerMenuOptions && !headerMenuOptions.hideFreezeColumnsCommand) {
hasFrozenOrResizeCommand = true;
if (columnHeaderMenuItems.filter(item => item !== 'divider' && item.hasOwnProperty('command') && item.command === 'freeze-columns').length === 0) {
columnHeaderMenuItems.push({
iconCssClass: headerMenuOptions.iconFreezeColumns || 'fa fa-thumb-tack',
title: options.enableTranslate ? this.translate.instant(`${translationPrefix}FREEZE_COLUMNS`) : this._locales && this._locales.TEXT_FREEZE_COLUMNS,
command: 'freeze-columns',
positionOrder: 47
});
}
}
// Column Resize by Content (column autofit)
if (headerMenuOptions && !headerMenuOptions.hideColumnResizeByContentCommand && this.sharedService.gridOptions.enableColumnResizeOnDoubleClick) {
hasFrozenOrResizeCommand = true;
if (!columnHeaderMenuItems.some(item => item !== 'divider' && item.hasOwnProperty('command') && item.command === 'column-resize-by-content')) {
columnHeaderMenuItems.push({
iconCssClass: headerMenuOptions.iconColumnResizeByContentCommand || 'fa fa-arrows-h',
title: this.extensionUtility.translateWhenEnabledAndServiceExist(`${translationPrefix}COLUMN_RESIZE_BY_CONTENT`, 'TEXT_COLUMN_RESIZE_BY_CONTENT'),
command: 'column-resize-by-content',
positionOrder: 48
});
}
}

// add a divider (separator) between the top freeze columns commands and the rest of the commands
if (columnHeaderMenuItems.filter(item => item !== 'divider' && item.positionOrder === 49).length === 0) {
columnHeaderMenuItems.push({ divider: true, command: '', positionOrder: 49 });
}
// add a divider (separator) between the top freeze columns commands and the rest of the commands
if (hasFrozenOrResizeCommand && !columnHeaderMenuItems.some(item => item !== 'divider' && item.positionOrder === 49)) {
columnHeaderMenuItems.push({ divider: true, command: '', positionOrder: 49 });
}

// Sorting Commands
Expand Down Expand Up @@ -221,7 +237,7 @@ export class HeaderMenuExtension implements Extension {

/** Hide a column from the grid */
hideColumn(column: Column) {
if (this.sharedService.grid && this.sharedService.grid.getColumns && this.sharedService.grid.setColumns && this.sharedService.grid.getColumnIndex) {
if (this.sharedService?.grid?.getColumnIndex) {
const columnIndex = this.sharedService.grid.getColumnIndex(column.id);
const currentVisibleColumns = this.sharedService.grid.getColumns() as Column[];

Expand Down Expand Up @@ -280,6 +296,9 @@ export class HeaderMenuExtension implements Extension {
columnHeaderMenuItems.forEach(item => {
if (item !== 'divider' && item.hasOwnProperty('command')) {
switch (item.command) {
case 'column-resize-by-content':
item.title = this.translate.instant(`${translationPrefix}COLUMN_RESIZE_BY_CONTENT`) || this._locales && this._locales.TEXT_COLUMN_RESIZE_BY_CONTENT;
break;
case 'clear-filter':
item.title = this.translate.instant(`${translationPrefix}REMOVE_FILTER`) || this._locales && this._locales.TEXT_REMOVE_FILTER;
break;
Expand Down Expand Up @@ -341,6 +360,9 @@ export class HeaderMenuExtension implements Extension {
case 'clear-sort':
this.clearColumnSort(event, args);
break;
case 'column-resize-by-content':
this.resizerService.handleSingleColumnResizeByContent(`${args.column.id}`);
break;
case 'freeze-columns':
const visibleColumns = [...this.sharedService.visibleColumns];
const columnPosition = visibleColumns.findIndex((col) => col.id === args.column.id);
Expand Down
19 changes: 13 additions & 6 deletions src/app/modules/angular-slickgrid/global-grid-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const GlobalGridOptions: Partial<GridOption> = {
enableCellNavigation: false,
enableColumnPicker: true,
enableColumnReorder: true,
enableColumnResizeOnDoubleClick: true,
enableContextMenu: true,
enableExcelExport: true, // Excel Export is the new default,
enableExport: false, // CSV/Text with Tab Delimited
Expand Down Expand Up @@ -179,6 +180,8 @@ export const GlobalGridOptions: Partial<GridOption> = {
iconSortAscCommand: 'fa fa-sort-amount-asc',
iconSortDescCommand: 'fa fa-sort-amount-desc',
iconColumnHideCommand: 'fa fa-times',
iconColumnResizeByContentCommand: 'fa fa-arrows-h',
hideColumnResizeByContentCommand: false,
hideColumnHideCommand: false,
hideClearFilterCommand: false,
hideClearSortCommand: false,
Expand Down Expand Up @@ -210,12 +213,16 @@ export const GlobalGridOptions: Partial<GridOption> = {
topPanelHeight: 35,
translationNamespaceSeparator: ':',
resizeByContentOnlyOnFirstLoad: true,
resizeAlwaysRecalculateColumnWidth: false,
resizeCellCharWidthInPx: 7.8,
resizeCellPaddingWidthInPx: 14,
resizeFormatterPaddingWidthInPx: 0,
resizeDefaultRatioForStringType: 0.88,
resizeMaxItemToInspectCellContentWidth: 1000,
resizeByContentOptions: {
alwaysRecalculateColumnWidth: false,
cellCharWidthInPx: 7.8,
cellPaddingWidthInPx: 14,
defaultRatioForStringType: 0.88,
formatterPaddingWidthInPx: 0,
maxItemToInspectCellContentWidth: 1000,
maxItemToInspectSingleColumnWidthByContent: 5000,
widthToRemoveFromExceededWidthReadjustment: 50,
},
treeDataOptions: {
exportIndentMarginLeft: 5,
exportIndentationLeadingChar: '͏͏͏͏͏͏͏͏͏·',
Expand Down
33 changes: 9 additions & 24 deletions src/app/modules/angular-slickgrid/models/gridOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
OperatorType,
OperatorString,
Pagination,
ResizeByContentOption,
RowDetailView,
RowMoveManager,
TreeDataOption,
Expand Down Expand Up @@ -239,6 +240,12 @@ export interface GridOption {
/** Defaults to true, which permits the user to move an entire column from a position to another. */
enableColumnReorder?: boolean | ColumnReorderFunction;

/**
* Defaults to true, when doing a double-click in the column resize section (top right of a column when the mouse resize icon shows up),
* do we want to automatically resize the column by its cell content?
*/
enableColumnResizeOnDoubleClick?: boolean;

/** Do we want to enable Context Menu? (mouse right+click) */
enableContextMenu?: boolean;

Expand Down Expand Up @@ -435,37 +442,15 @@ export interface GridOption {
/** Register 1 or more Slick Plugins */
registerPlugins?: any | any[];

/** defaults to false, if a column `width` is provided (or was previously calculated) should we recalculate it or not when resizing by cell content? */
resizeAlwaysRecalculateColumnWidth?: boolean;

/**
* defaults to true, do we want to resize the grid by content only on the first page or anytime the data changes?
* Requires `enableAutoResizeColumnsByCellContent` to be set.
* Also don't get confused with `autosizeColumnsByCellContentOnFirstLoad` that flag won't block resize by content after the first load while `resizeByContentOnlyOnFirstLoad`
*/
resizeByContentOnlyOnFirstLoad?: boolean;

/**
* Defaults to 7, width in pixels of a string character which is used by the resize columns by its content, this can vary depending on which font family/size is used & cell padding.
* This is only used when resizing the columns width by their content, we need to know the width of a character in pixel to do all calculations.
* Requires `enableAutoResizeColumnsByCellContent` to be set.
*/
resizeCellCharWidthInPx?: number;

/** Defaults to 6, cell padding width to add to the calculation when resizing columns by their cell text content (requires `enableAutoResizeColumnsByCellContent` to be set) */
resizeCellPaddingWidthInPx?: number;

/** Defaults to around ~0.9, what is the ratio to use (on field `type` "string" only) in the calculation when resizing columns by their cell text content (requires `enableAutoResizeColumnsByCellContent` to be set). */
resizeDefaultRatioForStringType?: number;

/** Defaults to 6, padding width to add to the calculation when using a Formatter and resizing columns by their cell text content (requires `enableAutoResizeColumnsByCellContent` to be set). */
resizeFormatterPaddingWidthInPx?: number;

/**
* Defaults to 1000, width in pixels of a string character which is used by the resize columns by its content, this can vary depending on which font family/size is used & cell padding.
* This is only used when resizing the columns width by their content, we need to know the width of a character in pixel to do all calculations.
*/
resizeMaxItemToInspectCellContentWidth?: number;
/** Resize by Content multiple options */
resizeByContentOptions?: ResizeByContentOption;

/** Row Detail View Plugin options & events (columnId, cssClass, toolTip, width) */
rowDetailView?: RowDetailView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export interface HeaderMenu {
/** A command identifier to be passed to the onCommand event handlers. */
command?: string;

/** Defaults to false, which will hide the "Column Resize by Content" command in the Header Menu (Grid Option "enableColumnResizeOnDoubleClick" has to also be enabled) */
hideColumnResizeByContentCommand?: boolean;

/** Defaults to false, which will hide the "Remove Filter" command in the Header Menu (Grid Option "enableHeaderMenu: true" has to be enabled) */
hideClearFilterCommand?: boolean;

Expand Down Expand Up @@ -56,6 +59,9 @@ export interface HeaderMenu {
/** icon for the "Hide Column" command */
iconColumnHideCommand?: string;

/** icon for the "Column Resize by Content" command */
iconColumnResizeByContentCommand?: string;

/** icon for the "Freeze Columns" command */
iconFreezeColumns?: string;

Expand Down
1 change: 1 addition & 0 deletions src/app/modules/angular-slickgrid/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export * from './operatorType.enum';
export * from './pagination.interface';
export * from './paginationChangedArgs.interface';
export * from './pagingInfo.interface';
export * from './resizeByContentOption.interface';
export * from './queryArgument.interface';
export * from './rowDetailView.interface';
export * from './rowMoveManager.interface';
Expand Down
3 changes: 3 additions & 0 deletions src/app/modules/angular-slickgrid/models/locale.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export interface Locale {
/** Text "Columns" title displayed in the Column Picker & Grid Menu (when enabled) */
TEXT_COLUMNS: string;

/** Text "Column Resize by Content" title displayed in the Header Menu */
TEXT_COLUMN_RESIZE_BY_CONTENT?: string;

/** Text "Commands" title displayed in the Column Picker & Grid Menu (when enabled) */
TEXT_COMMANDS: string;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export interface ResizeByContentOption {
/** defaults to false, if a column `width` is provided (or was previously calculated) should we recalculate it or not when resizing by cell content? */
alwaysRecalculateColumnWidth?: boolean;

/**
* Defaults to 7, width in pixels of a string character which is used by the resize columns by its content, this can vary depending on which font family/size is used & cell padding.
* This is only used when resizing the columns width by their content, we need to know the width of a character in pixel to do all calculations.
* Requires `enableAutoResizeColumnsByCellContent` to be set.
*/
cellCharWidthInPx?: number;

/** Defaults to 6, cell padding width to add to the calculation when resizing columns by their cell text content (requires `enableAutoResizeColumnsByCellContent` to be set) */
cellPaddingWidthInPx?: number;

/** Defaults to around ~0.9, what is the ratio to use (on field `type` "string" only) in the calculation when resizing columns by their cell text content (requires `enableAutoResizeColumnsByCellContent` to be set). */
defaultRatioForStringType?: number;

/** Defaults to 6, padding width to add to the calculation when using a Formatter and resizing columns by their cell text content (requires `enableAutoResizeColumnsByCellContent` to be set). */
formatterPaddingWidthInPx?: number;

/**
* Defaults to 1000, how many rows are we going to inspect cell content width?
* This is use when calculating all column width by their cell content, it requires `enableAutoResizeColumnsByCellContent` to be set.
*/
maxItemToInspectCellContentWidth?: number;

/**
* Defaults to 5000, how many rows (of a single column) are we going to inspect cell content width?
* This is use when calculating column width by their cell content when calling "Resize by Content" (from header menu and/or double-click to resize single column)
*/
maxItemToInspectSingleColumnWidthByContent?: number;

/** Defaults to 50, what width to remove from new column width when the grid is a frozen (pinned) grid and its column width exceeds the viewport full width. */
widthToRemoveFromExceededWidthReadjustment?: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ export interface SlickGrid {
/** Get the Viewport DOM node element */
getViewportNode(): HTMLElement;

/** Get all the Viewport node elements */
getViewports(): HTMLElement[];

/**
* Accepts a row integer and a cell integer, scrolling the view to the row where row is its row index, and cell is its cell index. Optionally accepts a forceEdit boolean which, if true, will attempt to initiate the edit dialogue for the field in the specified cell.
* Unlike setActiveCell, this scrolls the row into the viewport and sets the keyboard focus.
Expand Down Expand Up @@ -486,6 +489,7 @@ export interface SlickGrid {
onColumnsDrag: SlickEvent;
onColumnsReordered: SlickEvent;
onColumnsResized: SlickEvent;
onColumnsResizeDblClick: SlickEvent;
onContextMenu: SlickEvent;
onDrag: SlickEvent;
onDragEnd: SlickEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const DATAGRID_PAGINATION_HEIGHT = 35;
const gridId = 'grid1';
const gridUid = 'slickgrid_124343';
const containerId = 'demo-container';
declare const Slick: any;

const gridOptionMock = {
gridId,
Expand Down Expand Up @@ -43,6 +44,8 @@ const gridStub = {
getUID: () => gridUid,
reRenderColumns: jest.fn(),
setColumns: jest.fn(),
onColumnsResizeDblClick: new Slick.Event(),
onSort: new Slick.Event(),
};

// define a <div> container to simulate the grid container
Expand Down Expand Up @@ -78,6 +81,7 @@ describe('Resizer Service', () => {
createPreHeaderPanel: true,
showPreHeaderPanel: true,
preHeaderPanelHeight: 20,
resizeByContentOptions: {},
} as GridOption;
jest.spyOn(gridStub, 'getOptions').mockReturnValue(mockGridOptions);
jest.spyOn(gridStub, 'getContainerNode').mockReturnValue(div.querySelector(`#${gridId}`));
Expand Down Expand Up @@ -414,12 +418,12 @@ describe('Resizer Service', () => {
let mockData: any[];

beforeEach(() => {
mockGridOptions.resizeCellCharWidthInPx = 7;
mockGridOptions.resizeCellPaddingWidthInPx = 6;
mockGridOptions.resizeFormatterPaddingWidthInPx = 5;
mockGridOptions.resizeDefaultRatioForStringType = 0.88;
mockGridOptions.resizeAlwaysRecalculateColumnWidth = false;
mockGridOptions.resizeMaxItemToInspectCellContentWidth = 4;
mockGridOptions.resizeByContentOptions!.cellCharWidthInPx = 7;
mockGridOptions.resizeByContentOptions!.cellPaddingWidthInPx = 6;
mockGridOptions.resizeByContentOptions!.formatterPaddingWidthInPx = 5;
mockGridOptions.resizeByContentOptions!.defaultRatioForStringType = 0.88;
mockGridOptions.resizeByContentOptions!.alwaysRecalculateColumnWidth = false;
mockGridOptions.resizeByContentOptions!.maxItemToInspectCellContentWidth = 4;
mockColDefs = [
// typically the `originalWidth` is set by the columnDefinitiosn setter in vanilla grid bundle but we can mock it for our test
{ id: 'userId', field: 'userId', width: 30, originalWidth: 30 },
Expand All @@ -443,6 +447,17 @@ describe('Resizer Service', () => {
jest.spyOn(mockDataView, 'getItems').mockReturnValue(mockData);
});

it('should call handleSingleColumnResizeByContent when "onHeaderMenuColumnResizeByContent" gets triggered', () => {
const reRenderSpy = jest.spyOn(gridStub, 'reRenderColumns');

mockGridOptions.enableColumnResizeOnDoubleClick = true;
service.init(gridStub);
gridStub.onColumnsResizeDblClick.notify({ triggeredByColumn: 'firstName', grid: gridStub });

expect(reRenderSpy).toHaveBeenCalledWith(false);
expect(mockColDefs[1].width).toBe(56); // longest word "Destinee" (length 8 * charWidth(7) * ratio(0.88)) + cellPadding(6) = 55.28 ceil to => 56
});

it('should call the resize and expect first column have a fixed width while other will have a calculated width when resizing by their content', () => {
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
const reRenderColumnsSpy = jest.spyOn(gridStub, 'reRenderColumns');
Expand Down

0 comments on commit 183f33f

Please sign in to comment.