update scroll button

This commit is contained in:
Dan Percic 2021-08-07 18:55:27 +03:00
parent a84cc03dae
commit 9df7530e5d
8 changed files with 50 additions and 34 deletions

View File

@ -1,7 +1,7 @@
<button (click)="scroll(buttonType.top)" [hidden]="!showScroll(buttonType.top)" class="scroll-button top pointer">
<button (click)="scroll(buttonType.top)" [hidden]="(showScrollUp$ | async) === false" class="scroll-button top pointer">
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
</button>
<button (click)="scroll(buttonType.bottom)" [hidden]="!showScroll(buttonType.bottom)" class="scroll-button bottom pointer">
<button (click)="scroll(buttonType.bottom)" [hidden]="(showScrollDown$ | async) === false" class="scroll-button bottom pointer">
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
</button>

View File

@ -1,5 +1,7 @@
import { Component, HostListener, Input } from '@angular/core';
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { concatMap, delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
const ButtonTypes = {
top: 'top',
@ -13,28 +15,40 @@ type ButtonType = keyof typeof ButtonTypes;
templateUrl: './scroll-button.component.html',
styleUrls: ['./scroll-button.component.scss']
})
export class ScrollButtonComponent {
buttonType = ButtonTypes;
export class ScrollButtonComponent implements OnInit {
readonly buttonType = ButtonTypes;
@Input()
scrollViewport: CdkVirtualScrollViewport;
@Input()
itemSize: number;
showScrollUp$: Observable<boolean>;
showScrollDown$: Observable<boolean>;
ngOnInit() {
const scrollSize = () => this.scrollViewport.getDataLength() * this.itemSize;
const scrollIsNeeded = () => this.scrollViewport.getViewportSize() < scrollSize();
const reachedEnd = (type: ButtonType) => this.scrollViewport.measureScrollOffset(type) === 0;
const showScrollUp = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.top);
const showScrollDown = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.bottom);
const scroll$ = this.scrollViewport.elementScrolled().pipe(
startWith(''),
/** Delay first value so that we can wait for items to be rendered in viewport and get correct values */
concatMap((value, index) => (index === 0 ? of(value).pipe(delay(0)) : of(value)))
);
this.showScrollUp$ = scroll$.pipe(map(showScrollUp), distinctUntilChanged());
this.showScrollDown$ = scroll$.pipe(map(showScrollDown), distinctUntilChanged());
}
scroll(type: ButtonType): void {
const viewportSize = (this.scrollViewport?.getViewportSize() - this.itemSize) * (type === ButtonTypes.top ? -1 : 1);
const scrollOffset = this.scrollViewport?.measureScrollOffset('top');
this.scrollViewport?.scrollToOffset(scrollOffset + viewportSize, 'smooth');
}
showScroll(type: ButtonType): boolean {
const reachedEnd = this.scrollViewport?.measureScrollOffset(type) === 0;
const scrollSize = this.scrollViewport?.getDataLength() * this.itemSize;
const scrollIsNeeded = this.scrollViewport?.getViewportSize() < scrollSize;
return scrollIsNeeded && !reachedEnd;
}
@HostListener('document:keyup', ['$event'])
spaceAndPageDownScroll(event: KeyboardEvent): void {
if (['Space', 'PageDown'].includes(event.code) && (event.target as any).tagName === 'BODY') {

View File

@ -24,7 +24,7 @@
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-listing.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="let dw of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.pointer]="!!dw"

View File

@ -28,6 +28,7 @@ import {
dossierTemplateChecker
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { PermissionsService } from '@services/permissions.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
const isLeavingScreen = event => event instanceof NavigationStart && event.url !== '/main/dossiers';
@ -42,8 +43,8 @@ export class DossierListingScreenComponent
{
readonly itemSize = 95;
protected readonly _primaryKey = 'dossierName';
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
readonly buttonConfigs: ButtonConfig[] = [
{
label: _('dossier-listing.add-new'),
@ -76,6 +77,8 @@ export class DossierListingScreenComponent
private _lastScrollPosition: number;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ViewChild(CdkVirtualScrollViewport)
private readonly _scrollViewport: CdkVirtualScrollViewport;
constructor(
private readonly _router: Router,
@ -120,7 +123,7 @@ export class DossierListingScreenComponent
});
this.addSubscription = this._router.events.pipe(filter(isLeavingScreen)).subscribe(() => {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
this._lastScrollPosition = this._scrollViewport.measureScrollOffset('top');
});
}
@ -129,7 +132,7 @@ export class DossierListingScreenComponent
}
ngOnAttach() {
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._appStateService.reset();
this._loadEntitiesFromState();
this.ngOnInit();
@ -254,7 +257,7 @@ export class DossierListingScreenComponent
slug: 'dossierTemplateFilters',
label: this._translateService.instant('filters.dossier-templates'),
icon: 'red:template',
hide: this.filterService.getFilterGroup('dossierTemplateFilters')?.filters?.length <= 1,
hide: this.filterService.getGroup('dossierTemplateFilters')?.filters?.length <= 1,
filters: dossierTemplateFilters,
checker: dossierTemplateChecker
});

View File

@ -54,11 +54,11 @@
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-overview.no-match.title' | translate"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="let fileStatus of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.disabled]="fileStatus.isExcluded"
[class.last-opened]="isLastOpenedFile(fileStatus)"
[class.last-opened]="isLastOpenedFile(fileStatus.fileId)"
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
[routerLink]="fileLink(fileStatus)"
class="table-item"

View File

@ -32,6 +32,7 @@ import { fileStatusTranslations } from '../../translations/file-status-translati
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { annotationFilterChecker } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { PermissionsService } from '@services/permissions.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
@Component({
templateUrl: './dossier-overview-screen.component.html',
@ -42,11 +43,11 @@ export class DossierOverviewScreenComponent
extends BaseListingComponent<FileStatusWrapper>
implements OnInit, OnDestroy, OnDetach, OnAttach
{
readonly circleButtonTypes = CircleButtonTypes;
readonly itemSize = 80;
protected readonly _primaryKey = 'filename';
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser;
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
private readonly _lastOpenedFileKey = 'Dossier-Recent-' + this.activeDossier.dossierId;
readonly actionConfigs: ActionConfig[] = [
@ -97,7 +98,9 @@ export class DossierOverviewScreenComponent
private _lastScrollPosition: number;
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
private readonly _needsWorkTemplate: TemplateRef<any>;
@ViewChild('fileInput') private _fileInput: ElementRef;
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
@ViewChild(CdkVirtualScrollViewport)
private readonly _scrollViewport: CdkVirtualScrollViewport;
constructor(
private readonly _router: Router,
@ -126,14 +129,14 @@ export class DossierOverviewScreenComponent
}
get checkedRequiredFilters() {
return this.filterService.getFilterGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
return this.filterService.getGroup('quickFilters')?.filters.filter(f => f.required && f.checked);
}
get checkedNotRequiredFilters() {
return this.filterService.getFilterGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
}
isLastOpenedFile({ fileId }: FileStatusWrapper): boolean {
isLastOpenedFile(fileId: string): boolean {
return this._userPreferenceService.getLastOpenedFileId(this._lastOpenedFileKey) === fileId;
}
@ -157,7 +160,7 @@ export class DossierOverviewScreenComponent
.pipe(filter(event => event instanceof NavigationStart))
.subscribe((event: NavigationStart) => {
if (!event.url.endsWith(this._appStateService.activeDossierId)) {
this._lastScrollPosition = this.scrollViewport.measureScrollOffset('top');
this._lastScrollPosition = this._scrollViewport.measureScrollOffset('top');
}
});
@ -178,7 +181,7 @@ export class DossierOverviewScreenComponent
await this._appStateService.reloadActiveDossierFiles();
this._loadEntitiesFromState();
await this.ngOnInit();
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
this._scrollViewport.scrollTo({ top: this._lastScrollPosition });
}
ngOnDetach() {

View File

@ -21,7 +21,7 @@
[text]="'search-screen.no-data' | translate"
></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
<div
*cdkVirtualFor="let item of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
[class.pointer]="true"

View File

@ -1,4 +1,4 @@
import { Component, Injector, OnDestroy, ViewChild } from '@angular/core';
import { Component, Injector, OnDestroy } from '@angular/core';
import {
AutoUnsubscribeComponent,
EntitiesService,
@ -9,7 +9,6 @@ import {
SortingService,
TableColumnConfig
} from '@iqser/common-ui';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { combineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
@ -17,9 +16,6 @@ export const DefaultListingServices = [FilterService, SearchService, EntitiesSer
@Component({ template: '' })
export abstract class BaseListingComponent<T extends object> extends AutoUnsubscribeComponent implements OnDestroy {
@ViewChild(CdkVirtualScrollViewport)
readonly scrollViewport: CdkVirtualScrollViewport;
readonly filterService = this._injector.get(FilterService);
readonly searchService = this._injector.get<SearchService<T>>(SearchService);
readonly sortingService = this._injector.get<SortingService<T>>(SortingService);