Skip to content

Commit d72473c

Browse files
committed
feat: make table headers sticky and tables scrollable
* all tables now have a maximum height set and their scrollbar * headers are sticky * removed redundant extra div in attr def list, which caused some issues
1 parent eb7fcd3 commit d72473c

File tree

8 files changed

+133
-102
lines changed

8 files changed

+133
-102
lines changed

apps/admin-gui/src/_styles.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,3 +1679,10 @@ table .mdc-text-field--outlined {
16791679
.mdc-data-table__cell {
16801680
border-bottom: 1px solid rgba(0, 0, 0, 0.12) !important;
16811681
}
1682+
1683+
// sticky table headers
1684+
.mat-mdc-header-cell {
1685+
position: sticky !important;
1686+
top: 0;
1687+
z-index: 100;
1688+
}

apps/admin-gui/src/app/shared/components/attr-def-list/attr-def-list.component.html

Lines changed: 92 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -5,101 +5,99 @@
55
[pageSizeOptions]="pageSizeOptions"
66
[dataLength]="dataSource.filteredData.length"
77
[tableId]="tableId">
8-
<div class="overflow-auto">
9-
<table
10-
[dataSource]="dataSource"
11-
class="w-100"
12-
mat-table
13-
matSort
14-
matSortActive="displayName"
15-
matSortDirection="asc"
16-
matSortDisableClear>
17-
<ng-container
18-
matColumnDef="select"
19-
*ngIf="{all: dataSource | isAllSelected: selection.selected.length :canBeSelected} as selected">
20-
<th *matHeaderCellDef class="align-checkbox" mat-header-cell>
21-
<mat-checkbox
22-
(change)="$event ? masterToggle() : null"
23-
[aria-label]="selected.all | masterCheckboxLabel | translate"
24-
[checked]="selection.hasValue() && selected.all"
25-
[indeterminate]="selection.hasValue() && !selected.all"
26-
color="primary">
27-
</mat-checkbox>
28-
</th>
29-
<td *matCellDef="let row" class="static-column-size align-checkbox" mat-cell>
30-
<mat-checkbox
31-
attr.data-cy="{{row.displayName | multiWordDataCy}}-checkbox"
32-
(change)="$event ? selection.toggle(row) : null"
33-
(click)="$event.stopPropagation()"
34-
[aria-label]="selection.isSelected(row) | checkboxLabel | translate: {name: row.displayName}"
35-
[checked]="selection.isSelected(row)"
36-
[disabled]="row.namespace | consentRelatedAttribute:serviceEnabled:consentRequired"
37-
[matTooltip]="'ADMIN.ATTRIBUTES.CONSENT_RELATED_DISABLED' | translate"
38-
[matTooltipDisabled]="!(row.namespace | consentRelatedAttribute:serviceEnabled:consentRequired)"
39-
matTooltipPosition="left"
40-
color="primary">
41-
</mat-checkbox>
42-
</td>
43-
</ng-container>
44-
<ng-container matColumnDef="id">
45-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
46-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_ID' | translate}}
47-
</th>
48-
<td *matCellDef="let attrDef" class="static-column-size" mat-cell>{{attrDef.id}}</td>
49-
</ng-container>
50-
<ng-container matColumnDef="friendlyName">
51-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
52-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_FRIENDLY_NAME' | translate}}
53-
</th>
54-
<td
55-
attr.data-cy="{{attrDef.friendlyName | multiWordDataCy}}-friendly-name"
56-
*matCellDef="let attrDef"
57-
mat-cell>
58-
{{attrDef.friendlyName}}
59-
</td>
60-
</ng-container>
61-
<ng-container matColumnDef="displayName">
62-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
63-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_DISPLAY_NAME' | translate}}
64-
</th>
65-
<td mat-cell *matCellDef="let attribute">
66-
{{attribute.displayName}}
67-
</td>
68-
</ng-container>
69-
<ng-container matColumnDef="entity">
70-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
71-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_ENTITY' | translate}}
72-
</th>
73-
<td *matCellDef="let attrDef" mat-cell>{{attrDef.entity}}</td>
74-
</ng-container>
75-
<ng-container matColumnDef="namespace">
76-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
77-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_DEF' | translate}}
78-
</th>
79-
<td *matCellDef="let attrDef" mat-cell>{{attrDef.namespace | nameSpaceToDef}}</td>
80-
</ng-container>
81-
<ng-container matColumnDef="type">
82-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
83-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_TYPE' | translate}}
84-
</th>
85-
<td *matCellDef="let attrDef" mat-cell>{{attrDef.type | attributeTypeClean}}</td>
86-
</ng-container>
87-
<ng-container matColumnDef="unique">
88-
<th *matHeaderCellDef mat-header-cell mat-sort-header>
89-
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_UNIQUE' | translate}}
90-
</th>
91-
<td *matCellDef="let attrDef" mat-cell>{{attrDef.unique}}</td>
92-
</ng-container>
8+
<table
9+
[dataSource]="dataSource"
10+
class="w-100"
11+
mat-table
12+
matSort
13+
matSortActive="displayName"
14+
matSortDirection="asc"
15+
matSortDisableClear>
16+
<ng-container
17+
matColumnDef="select"
18+
*ngIf="{all: dataSource | isAllSelected: selection.selected.length :canBeSelected} as selected">
19+
<th *matHeaderCellDef class="align-checkbox" mat-header-cell>
20+
<mat-checkbox
21+
(change)="$event ? masterToggle() : null"
22+
[aria-label]="selected.all | masterCheckboxLabel | translate"
23+
[checked]="selection.hasValue() && selected.all"
24+
[indeterminate]="selection.hasValue() && !selected.all"
25+
color="primary">
26+
</mat-checkbox>
27+
</th>
28+
<td *matCellDef="let row" class="static-column-size align-checkbox" mat-cell>
29+
<mat-checkbox
30+
attr.data-cy="{{row.displayName | multiWordDataCy}}-checkbox"
31+
(change)="$event ? selection.toggle(row) : null"
32+
(click)="$event.stopPropagation()"
33+
[aria-label]="selection.isSelected(row) | checkboxLabel | translate: {name: row.displayName}"
34+
[checked]="selection.isSelected(row)"
35+
[disabled]="row.namespace | consentRelatedAttribute:serviceEnabled:consentRequired"
36+
[matTooltip]="'ADMIN.ATTRIBUTES.CONSENT_RELATED_DISABLED' | translate"
37+
[matTooltipDisabled]="!(row.namespace | consentRelatedAttribute:serviceEnabled:consentRequired)"
38+
matTooltipPosition="left"
39+
color="primary">
40+
</mat-checkbox>
41+
</td>
42+
</ng-container>
43+
<ng-container matColumnDef="id">
44+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
45+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_ID' | translate}}
46+
</th>
47+
<td *matCellDef="let attrDef" class="static-column-size" mat-cell>{{attrDef.id}}</td>
48+
</ng-container>
49+
<ng-container matColumnDef="friendlyName">
50+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
51+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_FRIENDLY_NAME' | translate}}
52+
</th>
53+
<td
54+
attr.data-cy="{{attrDef.friendlyName | multiWordDataCy}}-friendly-name"
55+
*matCellDef="let attrDef"
56+
mat-cell>
57+
{{attrDef.friendlyName}}
58+
</td>
59+
</ng-container>
60+
<ng-container matColumnDef="displayName">
61+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
62+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_DISPLAY_NAME' | translate}}
63+
</th>
64+
<td mat-cell *matCellDef="let attribute">
65+
{{attribute.displayName}}
66+
</td>
67+
</ng-container>
68+
<ng-container matColumnDef="entity">
69+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
70+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_ENTITY' | translate}}
71+
</th>
72+
<td *matCellDef="let attrDef" mat-cell>{{attrDef.entity}}</td>
73+
</ng-container>
74+
<ng-container matColumnDef="namespace">
75+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
76+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_DEF' | translate}}
77+
</th>
78+
<td *matCellDef="let attrDef" mat-cell>{{attrDef.namespace | nameSpaceToDef}}</td>
79+
</ng-container>
80+
<ng-container matColumnDef="type">
81+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
82+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_TYPE' | translate}}
83+
</th>
84+
<td *matCellDef="let attrDef" mat-cell>{{attrDef.type | attributeTypeClean}}</td>
85+
</ng-container>
86+
<ng-container matColumnDef="unique">
87+
<th *matHeaderCellDef mat-header-cell mat-sort-header>
88+
{{'ADMIN.ATTRIBUTES.TABLE_ATTR_UNIQUE' | translate}}
89+
</th>
90+
<td *matCellDef="let attrDef" mat-cell>{{attrDef.unique}}</td>
91+
</ng-container>
9392

94-
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
95-
<tr
96-
(click)="onRowClick(attrDef)"
97-
*matRowDef="let attrDef; columns: displayedColumns;"
98-
[class.cursor-pointer]="!disableRouting"
99-
class="dark-hover-list-item"
100-
mat-row></tr>
101-
</table>
102-
</div>
93+
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
94+
<tr
95+
(click)="onRowClick(attrDef)"
96+
*matRowDef="let attrDef; columns: displayedColumns;"
97+
[class.cursor-pointer]="!disableRouting"
98+
class="dark-hover-list-item"
99+
mat-row></tr>
100+
</table>
103101
</perun-web-apps-table-wrapper>
104102
</div>
105103

apps/consolidator/src/_styles.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,10 @@ mat-icon {
261261
white-space: nowrap !important;
262262
letter-spacing: normal;
263263
}
264+
265+
// sticky table headers
266+
.mat-mdc-header-cell {
267+
position: sticky !important;
268+
top: 0;
269+
z-index: 100;
270+
}

apps/publications/src/_styles.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,10 @@ mat-form-field mat-icon {
433433
.mdc-data-table__cell {
434434
border-bottom: 1px solid rgba(0, 0, 0, 0.12) !important;
435435
}
436+
437+
// sticky table headers
438+
.mat-mdc-header-cell {
439+
position: sticky !important;
440+
top: 0;
441+
z-index: 100;
442+
}

apps/user-profile/src/_styles.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,3 +332,10 @@ mat-list-item .mat-mdc-form-field-subscript-wrapper {
332332
.mdc-data-table__cell {
333333
border-bottom: 1px solid rgba(0, 0, 0, 0.12) !important;
334334
}
335+
336+
// sticky table headers
337+
.mat-mdc-header-cell {
338+
position: sticky !important;
339+
top: 0;
340+
z-index: 100;
341+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.table-height {
2+
max-height: 70vh;
3+
}

libs/perun/utils/src/lib/table-wrapper/table-wrapper.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
[allowExportAll]="allowExportAll"
1313
*ngIf="!hideExport"></perun-web-apps-table-options>
1414
</div>
15-
<div class="overflow-auto border-top">
15+
<div class="overflow-auto border-top table-height" #table>
1616
<ng-content></ng-content>
1717
</div>
1818

libs/perun/utils/src/lib/table-wrapper/table-wrapper.component.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class TableWrapperComponent implements OnInit {
3232
@Output() exportDisplayedData = new EventEmitter<string>();
3333
@Output() exportAllData = new EventEmitter<string>();
3434
@ViewChild('topNav') topNav: ElementRef<HTMLDivElement>;
35+
@ViewChild('table') table: ElementRef<HTMLDivElement>;
3536

3637
pageSize = 5;
3738
paginator: MatPaginator;
@@ -52,8 +53,14 @@ export class TableWrapperComponent implements OnInit {
5253
}
5354

5455
pageChangedTop(event: PageEvent): void {
55-
this.pageSize = event.pageSize;
56-
this.tableConfigService.setTablePageSize(this.tableId, event.pageSize);
56+
if (this.table) {
57+
this.pageSize = event.pageSize;
58+
this.tableConfigService.setTablePageSize(this.tableId, event.pageSize);
59+
this.table.nativeElement.scroll({
60+
top: 0,
61+
behavior: 'smooth',
62+
}); // scroll to top of table on page changes
63+
}
5764
}
5865

5966
pageChangedBottom(event: PageEvent): void {
@@ -64,10 +71,5 @@ export class TableWrapperComponent implements OnInit {
6471
if (this.tableId) {
6572
this.tableConfigService.setTablePageSize(this.tableId, event.pageSize);
6673
}
67-
this.topNav.nativeElement.scrollIntoView({
68-
behavior: 'smooth',
69-
block: 'start',
70-
inline: 'nearest',
71-
}); // scroll to top of table on page changes
7274
}
7375
}

0 commit comments

Comments
 (0)