Skip to content
Snippets Groups Projects
Commit a2186c78 authored by paf's avatar paf
Browse files

improve automatic view-mode adjustment, so it gets triggered whenever the...

improve automatic view-mode adjustment, so it gets triggered whenever the test-view table is to large vor the viewport (not as before on foxed break points in window-width). gets checked after every booklet load and window-resize, which should be the main reasons for changes in the table's width.

other reasons can change the table's with, like very long title of the featured unit, but in this case we don't want to wrap the whole view actually.
parent b0f2bcb9
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
} }
.adminbackground { .adminbackground {
flex: 10 0 900px; flex: 10 0 900px;
box-shadow: 5px 10px 20px black; box-shadow: 5px 10px 20px black;
background-color: white; background-color: white;
min-height: 85%; min-height: 85%;
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
padding: 25px; padding: 25px;
} }
.test-view-table-wrapper {
overflow-x: auto;
}
.page-header { .page-header {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
......
...@@ -56,29 +56,28 @@ ...@@ -56,29 +56,28 @@
<mat-icon>settings</mat-icon> <mat-icon>settings</mat-icon>
</button> </button>
<div class="test-view-table-wrapper">
<table class="test-view-table" matSort (matSortChange)="setTableSorting($event)">
<table class="test-view-table" matSort (matSortChange)="setTableSorting($event)"> <thead>
<tr class="mat-sort-container">
<td mat-sort-header="personLabel">Benutzer</td>
<td mat-sort-header="groupLabel" *ngIf="displayOptions.groupColumn === 'show'">Gruppe</td>
<td mat-sort-header="bookletName">Testheft</td>
<td mat-sort-header="timestamp">Aktivität</td>
</tr>
</thead>
<thead> <tc-test-view
<tr class="mat-sort-container"> *ngFor="let session of sessions$ | async; trackBy: trackSession"
<td mat-sort-header="personLabel">Benutzer</td> [testSession]="session"
<td mat-sort-header="groupLabel" *ngIf="displayOptions.groupColumn === 'show'">Gruppe</td> [displayOptions]="displayOptions"
<td mat-sort-header="bookletName">Testheft</td> (bookletId$)="adjustViewModeOnBookletLoad($event)"
<td mat-sort-header="timestamp">Aktivität</td> >
</tr> </tc-test-view>
</thead> </table>
</div>
<tc-test-view
*ngFor="let session of sessions$ | async; trackBy: trackSession"
[testSession]="session"
[displayOptions]="displayOptions"
>
</tc-test-view>
</table>
<!-- <p>Connected Admins: {{clientCount$ | async}}</p>-->
<!-- <pre>{{sessions$ | async | json}}</pre>-->
</div> </div>
</div> </div>
import {Component, HostListener, OnDestroy, OnInit} from '@angular/core'; import {Component, HostListener, OnDestroy, OnInit} from '@angular/core';
import {BackendService} from './backend.service'; import {BackendService} from './backend.service';
import {BehaviorSubject, combineLatest, Observable, Subject, Subscription} from 'rxjs'; import {BehaviorSubject, combineLatest, Observable, Subject, Subscription} from 'rxjs';
import {GroupData, TestSession, TestViewDisplayOptions, TestViewDisplayOptionKey} from './group-monitor.interfaces'; import {
GroupData,
TestSession,
TestViewDisplayOptions,
TestViewDisplayOptionKey,
} from './group-monitor.interfaces';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {ConnectionStatus} from './websocket-backend.service'; import {ConnectionStatus} from './websocket-backend.service';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
...@@ -14,6 +19,11 @@ import {Sort} from '@angular/material/sort'; ...@@ -14,6 +19,11 @@ import {Sort} from '@angular/material/sort';
styleUrls: ['./group-monitor.component.css'] styleUrls: ['./group-monitor.component.css']
}) })
export class GroupMonitorComponent implements OnInit, OnDestroy { export class GroupMonitorComponent implements OnInit, OnDestroy {
constructor(
private route: ActivatedRoute,
private bs: BackendService,
) {}
private routingSubscription: Subscription = null; private routingSubscription: Subscription = null;
ownGroup$: Observable<GroupData>; ownGroup$: Observable<GroupData>;
...@@ -28,14 +38,10 @@ export class GroupMonitorComponent implements OnInit, OnDestroy { ...@@ -28,14 +38,10 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
groupColumn: 'hide' groupColumn: 'hide'
}; };
constructor( private bookletIdsViewIsAdjustedFor: string[] = [];
private route: ActivatedRoute, private lastWindowSize = Infinity;
private bs: BackendService,
) {}
ngOnInit(): void { ngOnInit(): void {
this.autoSelectViewMode();
this.routingSubscription = this.route.params.subscribe(params => { this.routingSubscription = this.route.params.subscribe(params => {
this.ownGroup$ = this.bs.getGroupData(params['group-name']); this.ownGroup$ = this.bs.getGroupData(params['group-name']);
}); });
...@@ -58,42 +64,66 @@ export class GroupMonitorComponent implements OnInit, OnDestroy { ...@@ -58,42 +64,66 @@ export class GroupMonitorComponent implements OnInit, OnDestroy {
} }
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
autoSelectViewMode(): void { adjustViewModeOnWindowResize(): void {
const screenWidth = window.innerWidth; if (this.lastWindowSize > window.innerWidth) {
if (screenWidth > 1200) { this.shrinkViewIfNecessary();
this.displayOptions.view = 'full';
} else if (screenWidth > 800) {
this.displayOptions.view = 'medium';
} else { } else {
this.displayOptions.view = 'small'; this.growViewIfPossible();
} }
this.lastWindowSize = window.innerWidth;
} }
trackSession(index: number, session: TestSession): number { adjustViewModeOnBookletLoad(bookletId: string): void {
return session.personId * 10000 + session.testId; if (bookletId && this.bookletIdsViewIsAdjustedFor.indexOf(bookletId) === -1) {
this.bookletIdsViewIsAdjustedFor.push(bookletId);
this.growViewIfPossible();
}
} }
sortSessions(sort: Sort, sessions: TestSession[]): TestSession[] { private growViewIfPossible() {
return sessions.sort( if (this.getOverflow() <= 0) {
this.displayOptions.view = 'full';
setTimeout(() => this.shrinkViewIfNecessary(), 15);
}
}
private shrinkViewIfNecessary(): void {
if (this.getOverflow() > 0) {
if (this.displayOptions.view === 'full') {
this.displayOptions.view = 'medium';
setTimeout(() => this.shrinkViewIfNecessary(), 15);
} else if (this.displayOptions.view === 'medium') {
this.displayOptions.view = 'small';
}
}
}
(testSession1, testSession2) => { private getOverflow = (): number => {
const backgroundElement = document.querySelector('.adminbackground');
const testViewTableElement = document.querySelector('.test-view-table');
return testViewTableElement.scrollWidth - (backgroundElement.clientWidth - 50); // 50 = adminbackground's padding *2
}
if (sort.active === 'timestamp') { trackSession(index: number, session: TestSession): number {
return (testSession2.timestamp - testSession1.timestamp) * (sort.direction === 'asc' ? 1 : -1); return session.personId * 10000 + session.testId;
} }
const stringA = (testSession1[sort.active] || 'zzzzz'); sortSessions(sort: Sort, sessions: TestSession[]): TestSession[] {
const stringB = (testSession2[sort.active] || 'zzzzz'); return sessions
return stringA.localeCompare(stringB) * (sort.direction === 'asc' ? 1 : -1); .sort((testSession1, testSession2) => {
} if (sort.active === 'timestamp') {
); return (testSession2.timestamp - testSession1.timestamp) * (sort.direction === 'asc' ? 1 : -1);
}
const stringA = (testSession1[sort.active] || 'zzzzz');
const stringB = (testSession2[sort.active] || 'zzzzz');
return stringA.localeCompare(stringB) * (sort.direction === 'asc' ? 1 : -1);
});
} }
setTableSorting(sort: Sort): void { setTableSorting(sort: Sort): void {
if (!sort.active || sort.direction === '') { if (!sort.active || sort.direction === '') {
return; return;
} }
this.sortBy$.next(sort); this.sortBy$.next(sort);
} }
......
import {Component, Input, OnChanges, OnInit, SimpleChange} from '@angular/core'; import {Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChange} from '@angular/core';
import {BookletService} from '../booklet.service'; import {BookletService} from '../booklet.service';
import {combineLatest, Observable, Subject} from 'rxjs'; import {combineLatest, Observable, Subject, Subscription} from 'rxjs';
import {Booklet, TestSession, Testlet, Unit, TestViewDisplayOptions, BookletError} from '../group-monitor.interfaces'; import {Booklet, TestSession, Testlet, Unit, TestViewDisplayOptions, BookletError} from '../group-monitor.interfaces';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import {TestMode} from '../../config/test-mode'; import {TestMode} from '../../config/test-mode';
...@@ -28,17 +28,19 @@ interface UnitContext { ...@@ -28,17 +28,19 @@ interface UnitContext {
templateUrl: './test-view.component.html', templateUrl: './test-view.component.html',
styleUrls: ['./test-view.component.css', './test-view-table.css'] styleUrls: ['./test-view.component.css', './test-view-table.css']
}) })
export class TestViewComponent implements OnInit, OnChanges { export class TestViewComponent implements OnInit, OnChanges, OnDestroy {
@Input() testSession: TestSession; @Input() testSession: TestSession;
@Input() displayOptions: TestViewDisplayOptions; @Input() displayOptions: TestViewDisplayOptions;
@Output() bookletId$ = new EventEmitter<string>();
public testSession$: Subject<TestSession> = new Subject<TestSession>(); public testSession$: Subject<TestSession> = new Subject<TestSession>();
public booklet$: Observable<Booklet|BookletError>; public booklet$: Observable<Booklet|BookletError>;
public featuredUnit$: Observable<UnitContext|null>; public featuredUnit$: Observable<UnitContext|null>;
public maxTimeLeft: object|null; // TODO make observable maybe public maxTimeLeft: object|null; // TODO make observable maybe
private bookletSubscription: Subscription;
constructor( constructor(
private bookletsService: BookletService, private bookletsService: BookletService,
) { ) {
...@@ -49,6 +51,10 @@ export class TestViewComponent implements OnInit, OnChanges { ...@@ -49,6 +51,10 @@ export class TestViewComponent implements OnInit, OnChanges {
this.booklet$ = this.bookletsService.getBooklet(this.testSession.bookletName || ''); this.booklet$ = this.bookletsService.getBooklet(this.testSession.bookletName || '');
this.bookletSubscription = this.booklet$.subscribe((booklet: Booklet|BookletError) => {
this.bookletId$.emit(this.isBooklet(booklet) ? booklet.metadata.id : '');
});
this.featuredUnit$ = combineLatest<[Booklet|BookletError, TestSession]>([this.booklet$, this.testSession$]) this.featuredUnit$ = combineLatest<[Booklet|BookletError, TestSession]>([this.booklet$, this.testSession$])
.pipe(map((bookletAndSession: [Booklet|BookletError, TestSession]): UnitContext|null => { .pipe(map((bookletAndSession: [Booklet|BookletError, TestSession]): UnitContext|null => {
const booklet: Booklet|BookletError = bookletAndSession[0]; const booklet: Booklet|BookletError = bookletAndSession[0];
...@@ -74,6 +80,10 @@ export class TestViewComponent implements OnInit, OnChanges { ...@@ -74,6 +80,10 @@ export class TestViewComponent implements OnInit, OnChanges {
this.maxTimeLeft = this.parseJsonState(this.testSession.testState, 'MAXTIMELEFT'); this.maxTimeLeft = this.parseJsonState(this.testSession.testState, 'MAXTIMELEFT');
} }
ngOnDestroy() {
this.bookletSubscription.unsubscribe();
}
isBooklet(bookletOrBookletError: Booklet|BookletError): bookletOrBookletError is Booklet { isBooklet(bookletOrBookletError: Booklet|BookletError): bookletOrBookletError is Booklet {
return !('error' in bookletOrBookletError); return !('error' in bookletOrBookletError);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment