Skip to content

Commit d1c98e8

Browse files
committed
feat: resize column to fit content on double click
1 parent 06a9fe6 commit d1c98e8

File tree

5 files changed

+73
-3
lines changed

5 files changed

+73
-3
lines changed

projects/ngx-datatable/src/lib/components/body/body-row.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { DataTableBodyCellComponent } from './body-cell.component';
4343
<datatable-body-cell
4444
role="cell"
4545
tabindex="-1"
46+
[attr.header-id]="column.$$id"
4647
[row]="row"
4748
[group]="group"
4849
[expanded]="expanded"

projects/ngx-datatable/src/lib/components/datatable.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
(sort)="onColumnSort($event)"
2424
(resize)="onColumnResize($event)"
2525
(resizing)="onColumnResizing($event)"
26+
(fitToContent)="onColumnFitToContent($event)"
2627
(reorder)="onColumnReorder($event)"
2728
(select)="onHeaderSelect()"
2829
(columnContextmenu)="onColumnContextmenu($event)"

projects/ngx-datatable/src/lib/components/datatable.component.ts

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,7 @@ import { NGX_DATATABLE_CONFIG, NgxDatatableConfig } from '../ngx-datatable.confi
9999
]
100100
})
101101
export class DatatableComponent<TRow extends Row = any>
102-
implements OnInit, DoCheck, AfterViewInit, AfterContentInit, OnDestroy
103-
{
102+
implements OnInit, DoCheck, AfterViewInit, AfterContentInit, OnDestroy {
104103
private scrollbarHelper = inject(ScrollbarHelper);
105104
private cd = inject(ChangeDetectorRef);
106105
private columnChangesService = inject(ColumnChangesService);
@@ -906,7 +905,7 @@ export class DatatableComponent<TRow extends Row = any>
906905
}
907906

908907
/**
909-
* Recalulcates the column widths based on column width
908+
* Recalculates the column widths based on column width
910909
* distribution mode and scrollbar offsets.
911910
*/
912911
recalculateColumns(
@@ -1129,6 +1128,56 @@ export class DatatableComponent<TRow extends Row = any>
11291128
this.recalculateColumns(this._internalColumns, idx);
11301129
}
11311130

1131+
/**
1132+
* Triggered when double clicked on resize handler
1133+
*/
1134+
onColumnFitToContent({ headerElement, column }: { headerElement: HTMLElement, column: TableColumnInternal }): void {
1135+
1136+
const columnCells = Array.from(
1137+
this.bodyElement.nativeElement.querySelectorAll('datatable-body-cell')
1138+
);
1139+
1140+
const selectedColumnItems = columnCells.filter(c =>
1141+
c.getAttribute('header-id') === column.$$id
1142+
);
1143+
1144+
// this element has to be a form, otherwise form elements within a cell
1145+
// will be validated while being cloned. This can cause issues such as
1146+
// radio buttons being reset and losing their values.
1147+
const eDummyContainer = document.createElement('form');
1148+
// position fixed, so it isn't restricted to the boundaries of the parent
1149+
eDummyContainer.style.position = 'fixed';
1150+
eDummyContainer.style.padding = '16px';
1151+
1152+
const eBodyContainer = this.bodyElement.nativeElement;
1153+
1154+
selectedColumnItems.concat(headerElement).forEach(el => this.cloneItemIntoDummy(el, eDummyContainer));
1155+
1156+
eBodyContainer.appendChild(eDummyContainer);
1157+
1158+
const dummyContainerWidth = eDummyContainer.offsetWidth;
1159+
1160+
// we are finished with the dummy container, so get rid of it
1161+
eBodyContainer.removeChild(eDummyContainer);
1162+
1163+
this.onColumnResize({ column, newValue: dummyContainerWidth, prevValue: column.width });
1164+
}
1165+
1166+
private cloneItemIntoDummy(eCell: Element, eDummyContainer: HTMLElement): void {
1167+
// make a deep clone of the cell
1168+
const eCellClone: HTMLElement = eCell.cloneNode(true) as HTMLElement;
1169+
// the original has a fixed width, we remove this to allow the natural width based on content
1170+
eCellClone.style.width = '';
1171+
// the original has position = absolute, we need to remove this so it's positioned normally
1172+
eCellClone.style.position = 'static';
1173+
eCellClone.style.left = '';
1174+
1175+
const eCloneParent = document.createElement('div');
1176+
1177+
eCloneParent.append(eCellClone);
1178+
eDummyContainer.appendChild(eCloneParent);
1179+
}
1180+
11321181
/**
11331182
* The header triggered a column re-order event.
11341183
*/

projects/ngx-datatable/src/lib/components/header/header-cell.component.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ export class DataTableHeaderCellComponent implements OnInit, OnDestroy {
132132
}>(false);
133133
@Output() resize = new EventEmitter<{ width: number; column: TableColumnInternal }>();
134134
@Output() resizing = new EventEmitter<{ width: number; column: TableColumnInternal }>();
135+
@Output() fitToContent = new EventEmitter<HTMLElement>();
135136

136137
@HostBinding('class')
137138
get columnCssClasses(): string {
@@ -234,6 +235,15 @@ export class DataTableHeaderCellComponent implements OnInit, OnDestroy {
234235
this.onSort();
235236
}
236237

238+
@HostListener('dblclick', ['$event'])
239+
onDblClick(event: MouseEvent): void {
240+
const isHandle = (event.target as HTMLElement).classList.contains('resize-handle');
241+
if (isHandle) {
242+
event.stopPropagation();
243+
this.fitToContent.emit(this.element);
244+
}
245+
}
246+
237247
ngOnInit() {
238248
this.sortClass = this.calcSortClass(this.sortDir);
239249
// If there is already a default sort then start the counter with 1.

projects/ngx-datatable/src/lib/components/header/header.component.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import { OrderableDirective } from '../../directives/orderable.directive';
6060
role="columnheader"
6161
(resize)="onColumnResized($event)"
6262
(resizing)="onColumnResizing($event)"
63+
(fitToContent)="onColumnFitToContent($event, column)"
6364
long-press
6465
[pressModel]="column"
6566
[pressEnabled]="reorderable && column.draggable"
@@ -191,6 +192,10 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges {
191192
event: MouseEvent;
192193
column: TableColumnInternal;
193194
}>(false);
195+
@Output() fitToContent = new EventEmitter<{
196+
headerElement: HTMLElement;
197+
column: TableColumnInternal;
198+
}>();
194199

195200
_columnsByPin!: PinnedColumns[];
196201
_columnGroupWidths: any = {
@@ -267,6 +272,10 @@ export class DataTableHeaderComponent implements OnDestroy, OnChanges {
267272
this.resizing.emit(this.makeResizeEvent(width, column));
268273
}
269274

275+
onColumnFitToContent(headerElement: HTMLElement, column: TableColumnInternal) {
276+
this.fitToContent.emit({ headerElement, column });
277+
}
278+
270279
private makeResizeEvent(
271280
width: number,
272281
column: TableColumnInternal<Row>

0 commit comments

Comments
 (0)