Merge pull request #4889 from jsangmeister/categories-vscroll
Implemented Virtual Scrolling for Categories
This commit is contained in:
commit
bcf495e49f
@ -57,7 +57,7 @@ export abstract class BaseListViewComponent<V extends BaseViewModel> extends Bas
|
||||
titleService: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
protected storage?: StorageService
|
||||
protected storage: StorageService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
this.selectedRows = [];
|
||||
|
@ -20,6 +20,7 @@ import { PblNgridDataMatrixRow } from '@pebula/ngrid/target-events';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
|
||||
import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service';
|
||||
import { MediaManageService } from 'app/core/ui-services/media-manage.service';
|
||||
@ -185,6 +186,7 @@ export class MediafileListComponent extends BaseListViewComponent<ViewMediafile>
|
||||
titleService: Title,
|
||||
protected translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
storage: StorageService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
public repo: MediafileRepositoryService,
|
||||
@ -199,7 +201,7 @@ export class MediafileListComponent extends BaseListViewComponent<ViewMediafile>
|
||||
private groupRepo: GroupRepositoryService,
|
||||
private cd: ChangeDetectorRef
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
super(titleService, translate, matSnackBar, storage);
|
||||
this.canMultiSelect = true;
|
||||
|
||||
this.newDirectoryForm = this.formBuilder.group({
|
||||
|
@ -12,10 +12,43 @@
|
||||
</div>
|
||||
</os-head-bar>
|
||||
|
||||
<!-- Creating a new category -->
|
||||
<mat-card class="os-card" *ngIf="isCreatingNewCategory">
|
||||
<mat-card-title>New category</mat-card-title>
|
||||
<mat-card-content>
|
||||
<os-list-view-table
|
||||
[repo]="repo"
|
||||
[allowProjector]="false"
|
||||
[columns]="tableColumnDefinition"
|
||||
[filterProps]="filterProps"
|
||||
listStorageKey="category"
|
||||
(dataSourceChange)="onDataSourceChange($event)"
|
||||
>
|
||||
<!-- Title -->
|
||||
<div *pblNgridCellDef="'title'; row as category; rowContext as rowContext" class="cell-slot fill">
|
||||
<a
|
||||
class="detail-link"
|
||||
[routerLink]="category.id"
|
||||
(click)="saveScrollIndex('category', rowContext.identity)"
|
||||
></a>
|
||||
<div [style.margin-left]="getMargin(category)">{{ category.prefixedName }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Amount -->
|
||||
<div *pblNgridCellDef="'amount'; row as category" class="cell-slot fill">
|
||||
<span class="os-amount-chip">{{ category.motions.length }}</span>
|
||||
</div>
|
||||
</os-list-view-table>
|
||||
|
||||
<mat-menu #categoryMenu="matMenu">
|
||||
<button mat-menu-item [routerLink]="'./sort'">
|
||||
<mat-icon>sort</mat-icon>
|
||||
<span translate>Sort categories</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
<!-- Template for new motion block dialog -->
|
||||
<ng-template #newCategoryDialog>
|
||||
<h1 mat-dialog-title>
|
||||
<span translate>New category</span>
|
||||
</h1>
|
||||
<div class="os-form-card-mobile" mat-dialog-content>
|
||||
<form [formGroup]="createForm" (keydown)="onKeyDown($event)">
|
||||
<!-- Prefix -->
|
||||
<p>
|
||||
@ -28,64 +61,19 @@
|
||||
<p>
|
||||
<mat-form-field>
|
||||
<input formControlName="name" matInput placeholder="{{ 'Name' | translate }}" required />
|
||||
<mat-error *ngIf="!createForm.controls.name.valuid" translate>
|
||||
<mat-error *ngIf="!createForm.controls.name.valid" translate>
|
||||
A name is required
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
|
||||
<!-- Save and Cancel buttons -->
|
||||
<mat-card-actions>
|
||||
<button mat-button [disabled]="!createForm.valid" (click)="onCreate()">
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button [disabled]="!createForm.valid" [mat-dialog-close]="true">
|
||||
<span translate>Save</span>
|
||||
</button>
|
||||
<button mat-button (click)="onCancel()">
|
||||
<button mat-button [mat-dialog-close]="false">
|
||||
<span translate>Cancel</span>
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
<!-- Table -->
|
||||
<mat-card class="os-card">
|
||||
<table class="os-headed-listview-table" mat-table [dataSource]="dataSource">
|
||||
<!-- title column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<mat-header-cell *matHeaderCellDef>
|
||||
<span translate>Title</span>
|
||||
</mat-header-cell>
|
||||
<mat-cell *matCellDef="let category">
|
||||
<div [style.margin-left]="getMargin(category)">{{ category.prefixedName }}</div>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- amount column -->
|
||||
<ng-container matColumnDef="amount">
|
||||
<mat-header-cell *matHeaderCellDef>
|
||||
<span translate>Motions</span>
|
||||
</mat-header-cell>
|
||||
<mat-cell *matCellDef="let category">
|
||||
<span class="os-amount-chip">{{ category.motions.length }}</span>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Anchor column to open the separate tab -->
|
||||
<ng-container matColumnDef="anchor">
|
||||
<mat-header-cell *matHeaderCellDef></mat-header-cell>
|
||||
<mat-cell *matCellDef="let category">
|
||||
<a [routerLink]="category.id"></a>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="getColumnDefinition()"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: getColumnDefinition()"> </mat-row>
|
||||
</table>
|
||||
</mat-card>
|
||||
|
||||
<mat-menu #categoryMenu="matMenu">
|
||||
<button mat-menu-item [routerLink]="'./sort'">
|
||||
<mat-icon>sort</mat-icon>
|
||||
<span translate>Sort categories</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PblColumnDefinition } from '@pebula/ngrid';
|
||||
|
||||
import { OperatorService } from 'app/core/core-services/operator.service';
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
|
||||
import { BaseViewComponent } from 'app/site/base/base-view';
|
||||
import { infoDialogSettings } from 'app/shared/utils/dialog-settings';
|
||||
import { BaseListViewComponent } from 'app/site/base/base-list-view';
|
||||
import { ViewCategory } from 'app/site/motions/models/view-category';
|
||||
|
||||
/**
|
||||
@ -19,21 +22,33 @@ import { ViewCategory } from 'app/site/motions/models/view-category';
|
||||
templateUrl: './category-list.component.html',
|
||||
styleUrls: ['./category-list.component.scss']
|
||||
})
|
||||
export class CategoryListComponent extends BaseViewComponent implements OnInit {
|
||||
export class CategoryListComponent extends BaseListViewComponent<ViewCategory> implements OnInit {
|
||||
@ViewChild('newCategoryDialog', { static: true })
|
||||
private newCategoryDialog: TemplateRef<string>;
|
||||
|
||||
/**
|
||||
* Holds the create form
|
||||
*/
|
||||
public createForm: FormGroup;
|
||||
|
||||
/**
|
||||
* Table data Source
|
||||
* Define the columns to show
|
||||
*/
|
||||
public dataSource: MatTableDataSource<ViewCategory>;
|
||||
public tableColumnDefinition: PblColumnDefinition[] = [
|
||||
{
|
||||
prop: 'title',
|
||||
width: 'auto'
|
||||
},
|
||||
{
|
||||
prop: 'amount',
|
||||
width: this.singleButtonWidth
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Flag, if the creation panel is open
|
||||
* Define extra filter properties
|
||||
*/
|
||||
public isCreatingNewCategory = false;
|
||||
public filterProps = ['prefixedName'];
|
||||
|
||||
/**
|
||||
* helper for permission checks
|
||||
@ -59,11 +74,13 @@ export class CategoryListComponent extends BaseViewComponent implements OnInit {
|
||||
titleService: Title,
|
||||
translate: TranslateService,
|
||||
matSnackBar: MatSnackBar,
|
||||
private repo: CategoryRepositoryService,
|
||||
storage: StorageService,
|
||||
public repo: CategoryRepositoryService,
|
||||
private formBuilder: FormBuilder,
|
||||
private dialog: MatDialog,
|
||||
private operator: OperatorService
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
super(titleService, translate, matSnackBar, storage);
|
||||
|
||||
this.createForm = this.formBuilder.group({
|
||||
prefix: [''],
|
||||
@ -77,74 +94,46 @@ export class CategoryListComponent extends BaseViewComponent implements OnInit {
|
||||
*/
|
||||
public ngOnInit(): void {
|
||||
super.setTitle('Categories');
|
||||
|
||||
this.dataSource = new MatTableDataSource();
|
||||
this.repo.getViewModelListObservable().subscribe(viewCategories => {
|
||||
if (viewCategories && this.dataSource) {
|
||||
this.dataSource.data = viewCategories;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns that should be shown in the table
|
||||
*
|
||||
* @returns an array of strings building the column definition
|
||||
*/
|
||||
public getColumnDefinition(): string[] {
|
||||
return ['title', 'amount', 'anchor'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for the plus button
|
||||
*/
|
||||
public onPlusButton(): void {
|
||||
if (!this.isCreatingNewCategory) {
|
||||
this.createForm.reset();
|
||||
this.isCreatingNewCategory = true;
|
||||
const dialogRef = this.dialog.open(this.newCategoryDialog, infoDialogSettings);
|
||||
dialogRef.afterClosed().subscribe(res => {
|
||||
if (res) {
|
||||
this.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for the save button.
|
||||
* Sends the category to create to the repository and resets the form.
|
||||
* Sends the category to create to the repository.
|
||||
*/
|
||||
public onCreate(): void {
|
||||
private save(): void {
|
||||
if (this.createForm.valid) {
|
||||
try {
|
||||
this.repo.create(this.createForm.value);
|
||||
this.createForm.reset();
|
||||
this.isCreatingNewCategory = false;
|
||||
} catch (e) {
|
||||
this.raiseError(e);
|
||||
this.repo.create(this.createForm.value).catch(this.raiseError);
|
||||
}
|
||||
}
|
||||
// set a form control as "touched" to trigger potential error messages
|
||||
this.createForm.get('name').markAsTouched();
|
||||
}
|
||||
|
||||
/**
|
||||
* clicking Shift and Enter will save automatically
|
||||
* clicking Enter will save automatically
|
||||
* clicking Escape will cancel the process
|
||||
*
|
||||
* @param event has the code
|
||||
*/
|
||||
public onKeyDown(event: KeyboardEvent): void {
|
||||
if (event.key === 'Enter') {
|
||||
this.onCreate();
|
||||
this.save();
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.onCancel();
|
||||
this.dialog.closeAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the current form action
|
||||
*/
|
||||
public onCancel(): void {
|
||||
this.isCreatingNewCategory = false;
|
||||
}
|
||||
|
||||
public getMargin(category: ViewCategory): string {
|
||||
return `${category.level * 20}px`;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { Title } from '@angular/platform-browser';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PblColumnDefinition } from '@pebula/ngrid';
|
||||
|
||||
import { StorageService } from 'app/core/core-services/storage.service';
|
||||
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
|
||||
import { PromptService } from 'app/core/ui-services/prompt.service';
|
||||
import { Tag } from 'app/shared/models/core/tag';
|
||||
@ -68,13 +69,14 @@ export class TagListComponent extends BaseListViewComponent<ViewTag> implements
|
||||
public constructor(
|
||||
titleService: Title,
|
||||
matSnackBar: MatSnackBar,
|
||||
storage: StorageService,
|
||||
public repo: TagRepositoryService,
|
||||
protected translate: TranslateService, // protected required for ng-translate-extract
|
||||
private promptService: PromptService,
|
||||
private dialog: MatDialog,
|
||||
private formBuilder: FormBuilder
|
||||
) {
|
||||
super(titleService, translate, matSnackBar);
|
||||
super(titleService, translate, matSnackBar, storage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user