Compare commits

...

3 Commits

Author SHA1 Message Date
HardiReady 99304649ea Remove ngx-datatable dependency 2018-07-15 13:03:32 +02:00
HardiReady 8cabea74fd Add fixed header for scoreboard table 2018-07-15 12:59:37 +02:00
HardiReady dfaddfa992 * Use mat-table in highscores
* Fix fraction filter for scoreboard
2018-07-15 12:27:41 +02:00
10 changed files with 113 additions and 121 deletions

View File

@ -644,11 +644,6 @@
} }
} }
}, },
"@swimlane/ngx-datatable": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/@swimlane/ngx-datatable/-/ngx-datatable-13.0.1.tgz",
"integrity": "sha512-jjMEzQhXcdD+jfKNp+7U61lWx9ZzSGDn9QbpY6pJOJwz+E2CKeek6OouT5Qcc4MY4oFL9g/SZoPjLf90cbNIRw=="
},
"@types/jasmine": { "@types/jasmine": {
"version": "2.5.38", "version": "2.5.38",
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.38.tgz", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.38.tgz",

View File

@ -29,7 +29,6 @@
"@angular/platform-browser-dynamic": "^6.0.7", "@angular/platform-browser-dynamic": "^6.0.7",
"@angular/router": "^6.0.7", "@angular/router": "^6.0.7",
"@swimlane/ngx-charts": "^8.1.0", "@swimlane/ngx-charts": "^8.1.0",
"@swimlane/ngx-datatable": "^13.0.1",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"d3": "^4.11.0", "d3": "^4.11.0",
"file-saver": "^1.3.8", "file-saver": "^1.3.8",

View File

@ -11,71 +11,53 @@ h2 {
margin: 20px 0 0 10%; margin: 20px 0 0 10%;
} }
ngx-datatable { .highscore-table-container {
width: 345px; width: 320px;
margin: 3% 5% 0 5%; max-height: 394px;
height: 310px; margin: 100px 0 0 8%;
float: left; float: left;
border: solid #dfdfdf 1px;
border-radius: 10px 10px 2px 2px;
}
:host /deep/ .datatable-header {
width: 350px !important;
background: #222222;
font-weight: 700;
border-radius: 10px 10px 0 0;
color: white;
}
:host /deep/ span.datatable-header-cell-label, :host /deep/ div.datatable-body-cell-label {
padding-left: 8px;
}
:host /deep/ .ngx-datatable .datatable-header {
/*vertical center alignment*/
display: table-cell;
vertical-align: middle;
}
:host /deep/ .ngx-datatable .datatable-body .datatable-body-row > div {
/*vertical alignment*/
position: relative;
top: 10px;
}
:host /deep/ .datatable-body {
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto;
border: solid #dfdfdf 1px;
box-shadow: 3px 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
} }
:host /deep/ .datatable-body-row { .highscore-table {
color: #222222; width: 100%;
border-bottom: 1px solid grey;
} }
:host /deep/ .datatable-body-row:hover { table.mat-table img {
background-color: #f7f7f7; filter: invert(60%);
margin-left: -15px;
} }
:host /deep/ .ngx-datatable.fixed-header .datatable-header .datatable-header-inner .datatable-header-cell { :host /deep/ table.mat-table > thead {
margin: auto; position: absolute;
width: 320px;
display: inherit;
margin-left: -1px;
margin-top: -57px;
border: 1px solid #dadada;
}
.mat-column-kill, .mat-column-friendlyFire, .mat-column-revive, .mat-column-flagTouch,
.mat-column-vehicleLight, .mat-column-vehicleHeavy, .mat-column-vehicleAir, .mat-column-death, .mat-column-respawn {
width: 67px;
text-indent: 14px;
} }
/* Table Scrollbar BEGIN */ /* Table Scrollbar BEGIN */
:host /deep/ .ngx-datatable.scroll-vertical .datatable-body::-webkit-scrollbar { .highscore-table-container::-webkit-scrollbar {
width: 12px; width: 12px;
} }
:host /deep/ .ngx-datatable.scroll-vertical .datatable-body::-webkit-scrollbar-track { .highscore-table-container::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); border-left: 1px solid #f1f1f1;
border-radius: 10px;
} }
:host /deep/ .ngx-datatable.scroll-vertical .datatable-body::-webkit-scrollbar-thumb { .highscore-table-container::-webkit-scrollbar-thumb {
border-radius: 10px; background: rgb(234, 234, 234);
background: #4b4b4b;
-webkit-box-shadow: inset 0 0 6px rgba(255, 255, 255, 0.5);
} }
/* Table Scrollbar END */ /* Table Scrollbar END */

View File

@ -3,6 +3,7 @@
<div class="input-group search-field"> <div class="input-group search-field">
<input id="search-tasks" <input id="search-tasks"
markForCheck
placeholder="Spielername (mehrere mit '&' trennen)" placeholder="Spielername (mehrere mit '&' trennen)"
type="text" #query class="form-control" type="text" #query class="form-control"
(keyup.enter)="filterPlayers()" (keyup.enter)="filterPlayers()"
@ -16,41 +17,37 @@
</div> </div>
<div *ngFor="let attributeMap of playerAttributeDisplayNames"> <div *ngFor="let attributeMap of playerAttributeDisplayNames">
<ngx-datatable <div class="highscore-table-container">
[rows]="players[attributeMap.prop]" <table mat-table matSort
[messages]="emptyMessage" [dataSource]="players[attributeMap.prop]"
[headerHeight]="cellHeight" class="mat-elevation-z8 highscore-table">
[rowHeight]="cellHeight"
[cssClasses]='customClasses'
[columnMode]="'force'"
[scrollbarV]="true"
[selectionType]="'single'">
<ngx-datatable-column [width]="numberColWidth" name="#" prop="num"></ngx-datatable-column>
<ngx-datatable-column name="Spieler" prop="name" [width]="nameColWidth" style="padding-left:10px">
<ng-template ngx-datatable-cell-template let-row="row" let-value="value">
<span class="player-name"
[style.color]="row['fraction'] === 'BLUFOR' ? fraction.COLOR_BLUFOR : fraction.COLOR_OPFOR">
{{value}}
</span>
</ng-template>
</ngx-datatable-column>
<ngx-datatable-column [width]="valueColWidth" prop="{{attributeMap.prop}}"> <ng-container matColumnDef="num">
<ng-template ngx-datatable-header-template let-sort="sortFn"> <th mat-header-cell *matHeaderCellDef>#</th>
<span class="datatable-header-cell-wrapper"> <td mat-cell *matCellDef="let element">{{element.num}}</td>
<span class="datatable-header-cell-label text-truncate" matTooltip="{{attributeMap.head}}" (click)="sort()"> </ng-container>
<img src="../../../../assets/scoreboard/{{attributeMap.prop}}.png" alt="{{attributeMap.head}}">
</span> <ng-container matColumnDef="name">
</span> <th mat-header-cell *matHeaderCellDef>Name</th>
</ng-template> <td mat-cell *matCellDef="let element"
<ng-template ngx-datatable-cell-template let-row="row" let-value="value"> [style.color]="element['fraction'] === 'BLUFOR' ? fraction.COLOR_BLUFOR : fraction.COLOR_OPFOR">
<span style="padding-left: 7px;"> {{element.name}}
{{value}} </td>
</span> </ng-container>
</ng-template>
</ngx-datatable-column> <ng-container matColumnDef="{{attributeMap.prop}}">
</ngx-datatable> <th mat-header-cell *matHeaderCellDef>
<img src="../../../../assets/scoreboard/{{attributeMap.prop}}.png"
matTooltip="{{attributeMap.head}}"
alt="{{attributeMap.head}}">
</th>
<td mat-cell *matCellDef="let element"> {{element[attributeMap.prop]}}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="['num', 'name', attributeMap.prop];"></tr>
<tr mat-row *matRowDef="let row; columns: ['num', 'name', attributeMap.prop];"></tr>
</table>
</div>
</div> </div>
</div> </div>

View File

@ -30,21 +30,6 @@ export class StatisticHighScoreComponent implements OnInit {
playerAttributeDisplayNames = PlayerUtils.attributeDisplayNames.slice(2, PlayerUtils.attributeDisplayNames.length); playerAttributeDisplayNames = PlayerUtils.attributeDisplayNames.slice(2, PlayerUtils.attributeDisplayNames.length);
cellHeight = 40;
numberColWidth = 60;
nameColWidth = 210;
valueColWidth = 55;
emptyMessage = {emptyMessage: 'Keine Einträge'};
customClasses = {
sortAscending: 'glyphicon glyphicon-triangle-top',
sortDescending: 'glyphicon glyphicon-triangle-bottom',
};
readonly fraction = Fraction; readonly fraction = Fraction;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
@ -97,7 +82,6 @@ export class StatisticHighScoreComponent implements OnInit {
private filterPlayerAttribute(attribute) { private filterPlayerAttribute(attribute) {
const query = this.searchTerm.value.toLowerCase().split('&'); const query = this.searchTerm.value.toLowerCase().split('&');
return this.playersStored[attribute].filter(player => { return this.playersStored[attribute].filter(player => {
for (let i = 0; i < query.length; i++) { for (let i = 0; i < query.length; i++) {
if (query[i].trim() !== '' && player.name.toLowerCase().includes(query[i].trim())) { if (query[i].trim() !== '' && player.name.toLowerCase().includes(query[i].trim())) {
@ -107,5 +91,4 @@ export class StatisticHighScoreComponent implements OnInit {
return false; return false;
}); });
} }
} }

View File

@ -5,7 +5,6 @@ import {statsRouterModule, statsRoutingComponents} from './stats.routing';
import {WarService} from '../services/logs/war.service'; import {WarService} from '../services/logs/war.service';
import {NgxChartsModule} from '@swimlane/ngx-charts'; import {NgxChartsModule} from '@swimlane/ngx-charts';
import {CampaignService} from '../services/logs/campaign.service'; import {CampaignService} from '../services/logs/campaign.service';
import {NgxDatatableModule} from '@swimlane/ngx-datatable';
import {PlayerService} from '../services/logs/player.service'; import {PlayerService} from '../services/logs/player.service';
import {LogsService} from '../services/logs/logs.service'; import {LogsService} from '../services/logs/logs.service';
import {MatButtonModule, MatButtonToggleModule, MatExpansionModule, MatTableModule, MatSortModule} from '@angular/material'; import {MatButtonModule, MatButtonToggleModule, MatExpansionModule, MatTableModule, MatSortModule} from '@angular/material';
@ -13,8 +12,8 @@ import {MatButtonModule, MatButtonToggleModule, MatExpansionModule, MatTableModu
@NgModule({ @NgModule({
declarations: statsRoutingComponents, declarations: statsRoutingComponents,
imports: [CommonModule, SharedModule, statsRouterModule, NgxChartsModule, NgxDatatableModule, imports: [CommonModule, SharedModule, statsRouterModule, NgxChartsModule, MatButtonModule, MatExpansionModule,
MatButtonModule, MatExpansionModule, MatButtonToggleModule, MatTableModule, MatSortModule], MatButtonToggleModule, MatTableModule, MatSortModule],
providers: [WarService, CampaignService, PlayerService, LogsService] providers: [WarService, CampaignService, PlayerService, LogsService]
}) })
export class StatsModule { export class StatsModule {

View File

@ -1,3 +1,11 @@
.scoreboard-table {
height: 68vh;
width:fit-content;
border: 1px solid #dadada;
overflow-x: auto;
margin:auto;
}
.in-table-btn { .in-table-btn {
position: absolute; position: absolute;
margin-top: -5px; margin-top: -5px;
@ -12,6 +20,16 @@ table.mat-table img {
filter: invert(60%); filter: invert(60%);
} }
:host /deep/ table.mat-table > thead {
position: absolute;
display: inherit;
}
:host /deep/ table.mat-table > tbody {
margin-top: 60px;
display: block;
}
.mat-column-name { .mat-column-name {
width: 200px; width: 200px;
} }
@ -25,6 +43,18 @@ table.mat-table img {
text-indent: 9px; text-indent: 9px;
} }
th.mat-column-interact {
padding-left: 36px;
background: white;
position: relative;
z-index: 100;
}
/* TABLE SCROLLBAR */
div::-webkit-scrollbar-thumb {
border-top: solid white 56px;
}
/* MAT ICON BUTTON */ /* MAT ICON BUTTON */
:host/deep/.mat-table .mat-icon { :host/deep/.mat-table .mat-icon {

View File

@ -1,9 +1,9 @@
<div class="fade-in" style="border-top: 1px solid #dadada; overflow-x: auto"> <div class="fade-in scoreboard-table">
<table mat-table matSort <table mat-table matSort
[dataSource]="sortedRows" [dataSource]="sortedRows"
matSortActive="{{tableHead[2].prop}}" matSortDirection="asc" matSortDisableClear matSortActive="{{tableHead[2].prop}}" matSortDirection="asc" matSortDisableClear
(matSortChange)="sortData($event)" (matSortChange)="sortScoreboardData($event)"
class="mat-elevation-z8"> class="mat-elevation-z8">
<ng-container matColumnDef="{{tableHead[0].prop}}"> <ng-container matColumnDef="{{tableHead[0].prop}}">
@ -29,7 +29,7 @@
</ng-container> </ng-container>
<ng-container matColumnDef="interact"> <ng-container matColumnDef="interact">
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef>&nbsp;</th>
<td mat-cell *matCellDef="let element"> <td mat-cell *matCellDef="let element">
<button mat-icon-button <button mat-icon-button
matTooltip="Kampagnenstatistik für {{element.name}}" matTooltip="Kampagnenstatistik für {{element.name}}"

View File

@ -4,6 +4,7 @@ import {Fraction} from '../../../utils/fraction.enum';
import {PlayerUtils} from '../../../utils/player-utils'; import {PlayerUtils} from '../../../utils/player-utils';
import {saveAs} from 'file-saver/FileSaver'; import {saveAs} from 'file-saver/FileSaver';
import {MatSort, Sort} from '@angular/material'; import {MatSort, Sort} from '@angular/material';
import {SortUtils} from '../../../utils/sort-utils';
@Component({ @Component({
selector: 'cc-scoreboard', selector: 'cc-scoreboard',
@ -32,6 +33,8 @@ export class ScoreboardComponent implements OnChanges {
sortedRows = []; sortedRows = [];
currentSort = new MatSort();
displayedColumns = this.tableHead.map(head => head.prop); displayedColumns = this.tableHead.map(head => head.prop);
reorderable = false; reorderable = false;
@ -55,10 +58,8 @@ export class ScoreboardComponent implements OnChanges {
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes.war) { if (changes.war) {
this.rows = changes.war.currentValue.players; this.rows = changes.war.currentValue.players;
this.currentSort.active = 'kill';
const sort = new MatSort(); this.sortScoreboardData(this.currentSort);
sort.active = 'kill';
this.sortData(sort);
// this.elRef.nativeElement // this.elRef.nativeElement
// .querySelector('.datatable-body') // .querySelector('.datatable-body')
// .scrollTo(0, 0); // .scrollTo(0, 0);
@ -76,9 +77,13 @@ export class ScoreboardComponent implements OnChanges {
} else { } else {
this.rows = this.war.players; this.rows = this.war.players;
} }
this.sortScoreboardData(this.currentSort);
} }
sortData(sort: Sort) { sortScoreboardData(sort: MatSort) {
if (sort) {
this.currentSort = sort;
}
const data = this.rows.slice(); const data = this.rows.slice();
if (!sort.active || sort.direction === '') { if (!sort.active || sort.direction === '') {
this.sortedRows = data; this.sortedRows = data;
@ -88,14 +93,10 @@ export class ScoreboardComponent implements OnChanges {
this.sortedRows = data.sort((a, b) => { this.sortedRows = data.sort((a, b) => {
const isAsc = sort.direction === 'desc'; const isAsc = sort.direction === 'desc';
const sortProperty = sort.active; const sortProperty = sort.active;
return this.compare(a[sortProperty], b[sortProperty], isAsc); return SortUtils.compare(a[sortProperty], b[sortProperty], isAsc);
}); });
} }
compare(a, b, isAsc) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
exportCSV() { exportCSV() {
let csvOut = ''; let csvOut = '';
for (let i = 0; i < this.tableHead.length; i++) { for (let i = 0; i < this.tableHead.length; i++) {

View File

@ -0,0 +1,6 @@
export class SortUtils {
public static compare(a, b, isAsc) {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
}