diff --git a/projects/ngx-datatable/src/lib/components/body/body-row.component.ts b/projects/ngx-datatable/src/lib/components/body/body-row.component.ts index 6e550e3fe..e753b79f1 100644 --- a/projects/ngx-datatable/src/lib/components/body/body-row.component.ts +++ b/projects/ngx-datatable/src/lib/components/body/body-row.component.ts @@ -43,6 +43,7 @@ import { DataTableBodyCellComponent } from './body-cell.component'; } /** - * Recalulcates the column widths based on column width + * Recalculates the column widths based on column width * distribution mode and scrollbar offsets. */ recalculateColumns( @@ -1131,6 +1131,63 @@ export class DatatableComponent this.recalculateColumns(this._internalColumns, idx); } + /** + * Triggered when double clicked on resize handler + */ + onColumnFitToContent({ + headerElement, + column + }: { + headerElement: HTMLElement; + column: TableColumnInternal; + }): void { + const columnCells = Array.from( + this.bodyElement.nativeElement.querySelectorAll('datatable-body-cell') + ); + + const selectedColumnItems = columnCells.filter( + c => c.getAttribute('header-id') === column.$$id + ); + + // this element has to be a form, otherwise form elements within a cell + // will be validated while being cloned. This can cause issues such as + // radio buttons being reset and losing their values. + const eDummyContainer = document.createElement('form'); + // position fixed, so it isn't restricted to the boundaries of the parent + eDummyContainer.style.position = 'fixed'; + eDummyContainer.style.padding = '16px'; + + const eBodyContainer = this.bodyElement.nativeElement; + + selectedColumnItems + .concat(headerElement) + .forEach(el => this.cloneItemIntoDummy(el, eDummyContainer)); + + eBodyContainer.appendChild(eDummyContainer); + + const dummyContainerWidth = eDummyContainer.offsetWidth; + + // we are finished with the dummy container, so get rid of it + eBodyContainer.removeChild(eDummyContainer); + + this.onColumnResize({ column, newValue: dummyContainerWidth, prevValue: column.width }); + } + + private cloneItemIntoDummy(eCell: Element, eDummyContainer: HTMLElement): void { + // make a deep clone of the cell + const eCellClone: HTMLElement = eCell.cloneNode(true) as HTMLElement; + // the original has a fixed width, we remove this to allow the natural width based on content + eCellClone.style.width = ''; + // the original has position = absolute, we need to remove this so it's positioned normally + eCellClone.style.position = 'static'; + eCellClone.style.left = ''; + + const eCloneParent = document.createElement('div'); + + eCloneParent.append(eCellClone); + eDummyContainer.appendChild(eCloneParent); + } + /** * The header triggered a column re-order event. */ diff --git a/projects/ngx-datatable/src/lib/components/header/header-cell.component.ts b/projects/ngx-datatable/src/lib/components/header/header-cell.component.ts index 3a217080c..da6a5c294 100644 --- a/projects/ngx-datatable/src/lib/components/header/header-cell.component.ts +++ b/projects/ngx-datatable/src/lib/components/header/header-cell.component.ts @@ -140,6 +140,7 @@ export class DataTableHeaderCellComponent implements OnInit, OnDestroy { }>(false); @Output() readonly resize = new EventEmitter<{ width: number; column: TableColumnInternal }>(); @Output() readonly resizing = new EventEmitter<{ width: number; column: TableColumnInternal }>(); + @Output() readonly fitToContent = new EventEmitter(); @HostBinding('class') get columnCssClasses(): string { @@ -242,6 +243,15 @@ export class DataTableHeaderCellComponent implements OnInit, OnDestroy { this.onSort(); } + @HostListener('dblclick', ['$event']) + onDblClick(event: MouseEvent): void { + const isHandle = (event.target as HTMLElement).classList.contains('resize-handle'); + if (isHandle) { + event.stopPropagation(); + this.fitToContent.emit(this.element); + } + } + ngOnInit() { this.sortClass = this.calcSortClass(this.sortDir); // If there is already a default sort then start the counter with 1. diff --git a/projects/ngx-datatable/src/lib/components/header/header.component.ts b/projects/ngx-datatable/src/lib/components/header/header.component.ts index 7c2b676c4..d13a6ed36 100644 --- a/projects/ngx-datatable/src/lib/components/header/header.component.ts +++ b/projects/ngx-datatable/src/lib/components/header/header.component.ts @@ -85,6 +85,7 @@ import { DataTableHeaderCellComponent } from './header-cell.component'; [ariaHeaderCheckboxMessage]="ariaHeaderCheckboxMessage" (resize)="onColumnResized($event)" (resizing)="onColumnResizing($event)" + (fitToContent)="onColumnFitToContent($event, column)" (longPressStart)="onLongPressStart($event)" (longPressEnd)="onLongPressEnd($event)" (sort)="onSort($event)" @@ -199,6 +200,10 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { event: MouseEvent; column: TableColumnInternal; }>(false); + @Output() readonly fitToContent = new EventEmitter<{ + headerElement: HTMLElement; + column: TableColumnInternal; + }>(); _columnsByPin!: PinnedColumns[]; _columnGroupWidths: any = { @@ -275,6 +280,10 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges { this.resizing.emit(this.makeResizeEvent(width, column)); } + onColumnFitToContent(headerElement: HTMLElement, column: TableColumnInternal) { + this.fitToContent.emit({ headerElement, column }); + } + private makeResizeEvent( width: number, column: TableColumnInternal