I have an Angular table component utilizing the <table>
tag. The columns in the table utilize a directive for resizing their widths. The issue arises when I resize a column, as the width of nearly all other columns is affected. It appears that the table is imposing width constraints in the columns. Intead, I would like it to keep the columns widths intact, and resize itself if needed (it already has a horizontal scroll, but only appears when the columns are no fiting in the screen).
My component template:
<div
class="table-container"
class="{{ theme }}"
[class.without-upper-border]="removeHeaderBorder"
>
<div class="table-wrapper">
<table class="table" *ngIf="data" cellspacing="0">
<tr>
<th
class="header-cell"
*ngFor="let col of columns"
[ngStyle]="{ width: '150px' }"
appResizableColumn
>
{{ col.label }}
</th>
</tr>
<tr
class="row"
*ngFor="let row of displayedRows; let rowIndex = index"
[class.highlight]="rowIndex + 1 | isEven"
(contextmenu)="showRowContextMenu($event, rowIndex)"
>
<td
class="row-cell"
*ngFor="let col of columns"
(dblclick)="onCellSelection(rowIndex, col.attributeName)"
>
<ng-container
*ngIf="row[col.attributeName].editing; else elseTemplate"
>
<app-table-cell-input
class="cell-input"
[cell]="row[col.attributeName]"
(clickOutside)="onCellBlur(rowIndex, col.attributeName)"
(valueChanged)="
onCellValueChange(rowIndex, col.attributeName, $event)
"
></app-table-cell-input>
</ng-container>
<ng-template #elseTemplate>
<span class="text">
{{ row[col.attributeName].value || "" | square }}</span
>
</ng-template>
</td>
</tr>
</table>
</div>
<app-pagination-list
[items]="rowsSyncCopy"
(displayedItemsChanges)="updateDisplayedRows($event)"
></app-pagination-list>
</div>
My directive:
@Directive({
selector: '[appResizableColumn]',
})
export class ResizableColumnDirective {
startX: number = 0;
startWidth: number = 0;
resizeHandleWidth: number = 10;
isResizing: boolean = false;
constructor(private elementRef: ElementRef, private renderer: Renderer2) {}
@HostListener('mousedown', ['$event'])
onMouseDown(event: MouseEvent) {
const mouseX = event.pageX;
const rect = this.elementRef.nativeElement.getBoundingClientRect();
const rightEdge = rect.right;
if (rightEdge - mouseX <= this.resizeHandleWidth) {
this.startX = event.pageX;
this.startWidth = this.elementRef.nativeElement.offsetWidth;
this.isResizing = true;
this.renderer.addClass(document.body, 'resizing');
this.renderer.addClass(this.elementRef.nativeElement, 'resizing');
document.addEventListener('mousemove', this.onBorderDrag.bind(this));
document.addEventListener('mouseup', this.onMouseUp.bind(this));
}
}
onBorderDrag(event: MouseEvent) {
if (this.isResizing) {
const newWidth = this.startWidth + (event.clientX - this.startX);
this.renderer.setStyle(
this.elementRef.nativeElement,
'width',
newWidth + 'px'
);
}
}
onMouseUp(event: MouseEvent) {
if (this.isResizing) {
document.removeEventListener('mousemove', this.onMouseMove.bind(this));
document.removeEventListener('mouseup', this.onMouseUp.bind(this));
this.renderer.removeClass(document.body, 'resizing');
this.renderer.removeClass(this.elementRef.nativeElement, 'resizing');
this.isResizing = false;
}
}
}
My component scss:
.table-container {
color: color(grey-600);
border-radius: space(1);
width: 100%;
box-sizing: border-box;
position: relative;
width: 100%;
overflow: hidden;
&.without-upper-border {
border-radius: 0 0 space(1) space(1);
}
.table-wrapper {
overflow-x: auto;
}
.table {
min-width: 100%;
box-sizing: border-box;
table-layout: fixed;
}
.header-cell,
.row-cell {
border-top: 1px solid color(grey-200);
border-left: none;
margin: 0;
}
.header-cell {
position: relative;
font-weight: 400;
padding: space(2);
overflow: hidden;
background-color: transparent;
font-weight: 500;
color: color(grey-900);
white-space: nowrap;
text-align: left;
border-right: 1px solid transparent;
&:last-child {
border-right-style: none;
}
&::after {
content: "";
position: absolute;
top: 0;
bottom: 0;
width: 1px;
right: 0;
background-color: rgba(0, 0, 0, 0.2);
opacity: 0;
transition: 0.2s all;
}
&.near-right-border {
&::after {
opacity: 1;
}
cursor: col-resize !important;
}
}
.row {
transition: 0.2s all;
height: 34px;
background-color: transparent;
&:hover {
background-color: color(blue-50);
}
&:active {
background-color: color(blue-100);
}
&:last-child {
.row-cell:first-child {
border-radius: 0;
}
.row-cell:last-child {
border-radius: 0;
}
}
.row-cell {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 15rem;
background-color: transparent; // Background transparente
&:last-child {
border-right-style: none;
}
.text {
padding: space(1);
box-sizing: border-box;
}
.cell-input {
display: inline-block;
height: 100%;
width: 100%;
box-sizing: border-box;
}
}
}