update scroll button
This commit is contained in:
parent
a84cc03dae
commit
9df7530e5d
@ -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>
|
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
|
||||||
</button>
|
</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>
|
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -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 { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
import { concatMap, delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
|
||||||
const ButtonTypes = {
|
const ButtonTypes = {
|
||||||
top: 'top',
|
top: 'top',
|
||||||
@ -13,28 +15,40 @@ type ButtonType = keyof typeof ButtonTypes;
|
|||||||
templateUrl: './scroll-button.component.html',
|
templateUrl: './scroll-button.component.html',
|
||||||
styleUrls: ['./scroll-button.component.scss']
|
styleUrls: ['./scroll-button.component.scss']
|
||||||
})
|
})
|
||||||
export class ScrollButtonComponent {
|
export class ScrollButtonComponent implements OnInit {
|
||||||
buttonType = ButtonTypes;
|
readonly buttonType = ButtonTypes;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
scrollViewport: CdkVirtualScrollViewport;
|
scrollViewport: CdkVirtualScrollViewport;
|
||||||
@Input()
|
@Input()
|
||||||
itemSize: number;
|
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 {
|
scroll(type: ButtonType): void {
|
||||||
const viewportSize = (this.scrollViewport?.getViewportSize() - this.itemSize) * (type === ButtonTypes.top ? -1 : 1);
|
const viewportSize = (this.scrollViewport?.getViewportSize() - this.itemSize) * (type === ButtonTypes.top ? -1 : 1);
|
||||||
const scrollOffset = this.scrollViewport?.measureScrollOffset('top');
|
const scrollOffset = this.scrollViewport?.measureScrollOffset('top');
|
||||||
this.scrollViewport?.scrollToOffset(scrollOffset + viewportSize, 'smooth');
|
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'])
|
@HostListener('document:keyup', ['$event'])
|
||||||
spaceAndPageDownScroll(event: KeyboardEvent): void {
|
spaceAndPageDownScroll(event: KeyboardEvent): void {
|
||||||
if (['Space', 'PageDown'].includes(event.code) && (event.target as any).tagName === 'BODY') {
|
if (['Space', 'PageDown'].includes(event.code) && (event.target as any).tagName === 'BODY') {
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-listing.no-match.title' | translate"></redaction-empty-state>
|
<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
|
<div
|
||||||
*cdkVirtualFor="let dw of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
*cdkVirtualFor="let dw of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
||||||
[class.pointer]="!!dw"
|
[class.pointer]="!!dw"
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import {
|
|||||||
dossierTemplateChecker
|
dossierTemplateChecker
|
||||||
} from '@shared/components/filters/popup-filter/utils/filter-utils';
|
} from '@shared/components/filters/popup-filter/utils/filter-utils';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
const isLeavingScreen = event => event instanceof NavigationStart && event.url !== '/main/dossiers';
|
const isLeavingScreen = event => event instanceof NavigationStart && event.url !== '/main/dossiers';
|
||||||
|
|
||||||
@ -42,8 +43,8 @@ export class DossierListingScreenComponent
|
|||||||
{
|
{
|
||||||
readonly itemSize = 95;
|
readonly itemSize = 95;
|
||||||
protected readonly _primaryKey = 'dossierName';
|
protected readonly _primaryKey = 'dossierName';
|
||||||
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
|
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
|
readonly tableHeaderLabel = _('dossier-listing.table-header.title');
|
||||||
readonly buttonConfigs: ButtonConfig[] = [
|
readonly buttonConfigs: ButtonConfig[] = [
|
||||||
{
|
{
|
||||||
label: _('dossier-listing.add-new'),
|
label: _('dossier-listing.add-new'),
|
||||||
@ -76,6 +77,8 @@ export class DossierListingScreenComponent
|
|||||||
private _lastScrollPosition: number;
|
private _lastScrollPosition: number;
|
||||||
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
|
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
|
||||||
private readonly _needsWorkTemplate: TemplateRef<any>;
|
private readonly _needsWorkTemplate: TemplateRef<any>;
|
||||||
|
@ViewChild(CdkVirtualScrollViewport)
|
||||||
|
private readonly _scrollViewport: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
@ -120,7 +123,7 @@ export class DossierListingScreenComponent
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.addSubscription = this._router.events.pipe(filter(isLeavingScreen)).subscribe(() => {
|
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() {
|
ngOnAttach() {
|
||||||
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
|
this._scrollViewport.scrollTo({ top: this._lastScrollPosition });
|
||||||
this._appStateService.reset();
|
this._appStateService.reset();
|
||||||
this._loadEntitiesFromState();
|
this._loadEntitiesFromState();
|
||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
@ -254,7 +257,7 @@ export class DossierListingScreenComponent
|
|||||||
slug: 'dossierTemplateFilters',
|
slug: 'dossierTemplateFilters',
|
||||||
label: this._translateService.instant('filters.dossier-templates'),
|
label: this._translateService.instant('filters.dossier-templates'),
|
||||||
icon: 'red:template',
|
icon: 'red:template',
|
||||||
hide: this.filterService.getFilterGroup('dossierTemplateFilters')?.filters?.length <= 1,
|
hide: this.filterService.getGroup('dossierTemplateFilters')?.filters?.length <= 1,
|
||||||
filters: dossierTemplateFilters,
|
filters: dossierTemplateFilters,
|
||||||
checker: dossierTemplateChecker
|
checker: dossierTemplateChecker
|
||||||
});
|
});
|
||||||
|
|||||||
@ -54,11 +54,11 @@
|
|||||||
|
|
||||||
<redaction-empty-state *ngIf="noMatch$ | async" [text]="'dossier-overview.no-match.title' | translate"></redaction-empty-state>
|
<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
|
<div
|
||||||
*cdkVirtualFor="let fileStatus of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
*cdkVirtualFor="let fileStatus of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
||||||
[class.disabled]="fileStatus.isExcluded"
|
[class.disabled]="fileStatus.isExcluded"
|
||||||
[class.last-opened]="isLastOpenedFile(fileStatus)"
|
[class.last-opened]="isLastOpenedFile(fileStatus.fileId)"
|
||||||
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
|
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
|
||||||
[routerLink]="fileLink(fileStatus)"
|
[routerLink]="fileLink(fileStatus)"
|
||||||
class="table-item"
|
class="table-item"
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import { fileStatusTranslations } from '../../translations/file-status-translati
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { annotationFilterChecker } from '@shared/components/filters/popup-filter/utils/filter-utils';
|
import { annotationFilterChecker } from '@shared/components/filters/popup-filter/utils/filter-utils';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './dossier-overview-screen.component.html',
|
templateUrl: './dossier-overview-screen.component.html',
|
||||||
@ -42,11 +43,11 @@ export class DossierOverviewScreenComponent
|
|||||||
extends BaseListingComponent<FileStatusWrapper>
|
extends BaseListingComponent<FileStatusWrapper>
|
||||||
implements OnInit, OnDestroy, OnDetach, OnAttach
|
implements OnInit, OnDestroy, OnDetach, OnAttach
|
||||||
{
|
{
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
|
||||||
readonly itemSize = 80;
|
readonly itemSize = 80;
|
||||||
protected readonly _primaryKey = 'filename';
|
protected readonly _primaryKey = 'filename';
|
||||||
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
|
readonly tableHeaderLabel = _('dossier-overview.table-header.title');
|
||||||
private readonly _lastOpenedFileKey = 'Dossier-Recent-' + this.activeDossier.dossierId;
|
private readonly _lastOpenedFileKey = 'Dossier-Recent-' + this.activeDossier.dossierId;
|
||||||
|
|
||||||
readonly actionConfigs: ActionConfig[] = [
|
readonly actionConfigs: ActionConfig[] = [
|
||||||
@ -97,7 +98,9 @@ export class DossierOverviewScreenComponent
|
|||||||
private _lastScrollPosition: number;
|
private _lastScrollPosition: number;
|
||||||
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
|
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
|
||||||
private readonly _needsWorkTemplate: TemplateRef<any>;
|
private readonly _needsWorkTemplate: TemplateRef<any>;
|
||||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
@ViewChild('fileInput') private readonly _fileInput: ElementRef;
|
||||||
|
@ViewChild(CdkVirtualScrollViewport)
|
||||||
|
private readonly _scrollViewport: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
@ -126,14 +129,14 @@ export class DossierOverviewScreenComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
get checkedRequiredFilters() {
|
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() {
|
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;
|
return this._userPreferenceService.getLastOpenedFileId(this._lastOpenedFileKey) === fileId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +160,7 @@ export class DossierOverviewScreenComponent
|
|||||||
.pipe(filter(event => event instanceof NavigationStart))
|
.pipe(filter(event => event instanceof NavigationStart))
|
||||||
.subscribe((event: NavigationStart) => {
|
.subscribe((event: NavigationStart) => {
|
||||||
if (!event.url.endsWith(this._appStateService.activeDossierId)) {
|
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();
|
await this._appStateService.reloadActiveDossierFiles();
|
||||||
this._loadEntitiesFromState();
|
this._loadEntitiesFromState();
|
||||||
await this.ngOnInit();
|
await this.ngOnInit();
|
||||||
this.scrollViewport.scrollTo({ top: this._lastScrollPosition });
|
this._scrollViewport.scrollTo({ top: this._lastScrollPosition });
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDetach() {
|
ngOnDetach() {
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
[text]="'search-screen.no-data' | translate"
|
[text]="'search-screen.no-data' | translate"
|
||||||
></redaction-empty-state>
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
|
||||||
<div
|
<div
|
||||||
*cdkVirtualFor="let item of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
*cdkVirtualFor="let item of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
||||||
[class.pointer]="true"
|
[class.pointer]="true"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, Injector, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, Injector, OnDestroy } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
AutoUnsubscribeComponent,
|
AutoUnsubscribeComponent,
|
||||||
EntitiesService,
|
EntitiesService,
|
||||||
@ -9,7 +9,6 @@ import {
|
|||||||
SortingService,
|
SortingService,
|
||||||
TableColumnConfig
|
TableColumnConfig
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
||||||
import { combineLatest, Observable } from 'rxjs';
|
import { combineLatest, Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
|
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@ -17,9 +16,6 @@ export const DefaultListingServices = [FilterService, SearchService, EntitiesSer
|
|||||||
|
|
||||||
@Component({ template: '' })
|
@Component({ template: '' })
|
||||||
export abstract class BaseListingComponent<T extends object> extends AutoUnsubscribeComponent implements OnDestroy {
|
export abstract class BaseListingComponent<T extends object> extends AutoUnsubscribeComponent implements OnDestroy {
|
||||||
@ViewChild(CdkVirtualScrollViewport)
|
|
||||||
readonly scrollViewport: CdkVirtualScrollViewport;
|
|
||||||
|
|
||||||
readonly filterService = this._injector.get(FilterService);
|
readonly filterService = this._injector.get(FilterService);
|
||||||
readonly searchService = this._injector.get<SearchService<T>>(SearchService);
|
readonly searchService = this._injector.get<SearchService<T>>(SearchService);
|
||||||
readonly sortingService = this._injector.get<SortingService<T>>(SortingService);
|
readonly sortingService = this._injector.get<SortingService<T>>(SortingService);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user