Pull request #190: add scroll button
Merge in RED/ui from RED-1486 to master * commit '4f8b2adaf32f82881ad290d8a68f8f66743d950b': add scroll button
This commit is contained in:
commit
b9f8d28e8d
@ -18,6 +18,7 @@ export class IconsModule {
|
|||||||
'analyse',
|
'analyse',
|
||||||
'approved',
|
'approved',
|
||||||
'arrow-down',
|
'arrow-down',
|
||||||
|
'arrow-down-o',
|
||||||
'arrow-right',
|
'arrow-right',
|
||||||
'arrow-up',
|
'arrow-up',
|
||||||
'assign',
|
'assign',
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
<button class="scroll-button pointer" *ngIf="showScrollButton()" (click)="scroll()">
|
||||||
|
<mat-icon svgIcon="red:arrow-down-o"></mat-icon>
|
||||||
|
</button>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
@import 'apps/red-ui/src/assets/styles/red-variables';
|
||||||
|
|
||||||
|
.scroll-button {
|
||||||
|
background-color: $white;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 0;
|
||||||
|
height: 40px;
|
||||||
|
width: 44px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px 0 0 8px;
|
||||||
|
box-shadow: -1px 1px 5px 0 rgba(40, 50, 65, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
color: $grey-7;
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import { Component, HostListener, Input } from '@angular/core';
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-scroll-button',
|
||||||
|
templateUrl: './scroll-button.component.html',
|
||||||
|
styleUrls: ['./scroll-button.component.scss']
|
||||||
|
})
|
||||||
|
export class ScrollButtonComponent {
|
||||||
|
@Input()
|
||||||
|
scrollViewport: CdkVirtualScrollViewport;
|
||||||
|
@Input()
|
||||||
|
itemSize: number;
|
||||||
|
|
||||||
|
scroll(): void {
|
||||||
|
const scrollOffset = this.scrollViewport.measureScrollOffset('top');
|
||||||
|
const ligaturePortion = 50;
|
||||||
|
const viewportSize = this.scrollViewport.getViewportSize() - ligaturePortion;
|
||||||
|
|
||||||
|
this.scrollViewport.scrollToOffset(scrollOffset + viewportSize, 'smooth');
|
||||||
|
}
|
||||||
|
|
||||||
|
showScrollButton(): boolean {
|
||||||
|
const reachedBottom = this.scrollViewport.measureScrollOffset('bottom') === 0;
|
||||||
|
const scrollSize = this.scrollViewport.getDataLength() * this.itemSize;
|
||||||
|
const scrollIsNeeded = this.scrollViewport.getViewportSize() < scrollSize;
|
||||||
|
|
||||||
|
return scrollIsNeeded && !reachedBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('document:keyup', ['$event'])
|
||||||
|
spaceAndPageDownScroll(event: KeyboardEvent): void {
|
||||||
|
if (['Space', 'PageDown'].includes(event.code)) {
|
||||||
|
this.scroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -44,6 +44,7 @@ import { EditProjectDictionaryComponent } from './dialogs/edit-project-dialog/di
|
|||||||
import { EditProjectTeamMembersComponent } from './dialogs/edit-project-dialog/team-members/edit-project-team-members.component';
|
import { EditProjectTeamMembersComponent } from './dialogs/edit-project-dialog/team-members/edit-project-team-members.component';
|
||||||
import { TeamMembersManagerComponent } from './components/team-members-manager/team-members-manager.component';
|
import { TeamMembersManagerComponent } from './components/team-members-manager/team-members-manager.component';
|
||||||
import { TeamMembersDialogComponent } from './dialogs/team-members-dialog/team-members-dialog.component';
|
import { TeamMembersDialogComponent } from './dialogs/team-members-dialog/team-members-dialog.component';
|
||||||
|
import { ScrollButtonComponent } from './components/scroll-button/scroll-button.component';
|
||||||
|
|
||||||
const screens = [
|
const screens = [
|
||||||
ProjectListingScreenComponent,
|
ProjectListingScreenComponent,
|
||||||
@ -59,7 +60,8 @@ const dialogs = [
|
|||||||
ForceRedactionDialogComponent,
|
ForceRedactionDialogComponent,
|
||||||
RemoveAnnotationsDialogComponent,
|
RemoveAnnotationsDialogComponent,
|
||||||
DocumentInfoDialogComponent,
|
DocumentInfoDialogComponent,
|
||||||
AssignReviewerApproverDialogComponent
|
AssignReviewerApproverDialogComponent,
|
||||||
|
DossierDictionaryDialogComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
@ -84,6 +86,7 @@ const components = [
|
|||||||
EditProjectDictionaryComponent,
|
EditProjectDictionaryComponent,
|
||||||
EditProjectTeamMembersComponent,
|
EditProjectTeamMembersComponent,
|
||||||
TeamMembersManagerComponent,
|
TeamMembersManagerComponent,
|
||||||
|
ScrollButtonComponent,
|
||||||
|
|
||||||
...screens,
|
...screens,
|
||||||
...dialogs
|
...dialogs
|
||||||
@ -101,7 +104,7 @@ const services = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [...components, DossierDictionaryDialogComponent],
|
declarations: [...components],
|
||||||
providers: [...services],
|
providers: [...services],
|
||||||
imports: [CommonModule, SharedModule, FileUploadDownloadModule, ProjectsRoutingModule]
|
imports: [CommonModule, SharedModule, FileUploadDownloadModule, ProjectsRoutingModule]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -104,7 +104,7 @@
|
|||||||
type="no-match"
|
type="no-match"
|
||||||
></redaction-empty-state>
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="85" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
|
||||||
<div
|
<div
|
||||||
*cdkVirtualFor="
|
*cdkVirtualFor="
|
||||||
let pw of displayedEntities
|
let pw of displayedEntities
|
||||||
@ -169,6 +169,12 @@
|
|||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
|
|
||||||
|
<redaction-scroll-button
|
||||||
|
[scrollViewport]="scrollBar"
|
||||||
|
[itemSize]="itemSize"
|
||||||
|
*ngIf="scrollBar && itemSize > 0"
|
||||||
|
></redaction-scroll-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-container" redactionHasScrollbar>
|
<div class="right-container" redactionHasScrollbar>
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
@import '../../../../../assets/styles/red-variables';
|
@import '../../../../../assets/styles/red-variables';
|
||||||
|
|
||||||
.content-container {
|
.content-container {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
cdk-virtual-scroll-viewport {
|
cdk-virtual-scroll-viewport {
|
||||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||||
grid-template-columns: 2fr 1fr 1fr auto 11px;
|
grid-template-columns: 2fr 1fr 1fr auto 11px;
|
||||||
|
|||||||
@ -47,12 +47,13 @@ export class ProjectListingScreenComponent
|
|||||||
} = {
|
} = {
|
||||||
statusFilters: []
|
statusFilters: []
|
||||||
};
|
};
|
||||||
|
readonly itemSize = 85;
|
||||||
protected readonly _searchKey = 'name';
|
protected readonly _searchKey = 'name';
|
||||||
protected readonly _sortKey = 'project-listing';
|
protected readonly _sortKey = 'project-listing';
|
||||||
private _projectAutoUpdateTimer: Subscription;
|
private _projectAutoUpdateTimer: Subscription;
|
||||||
private _lastScrollPosition: number;
|
private _lastScrollPosition: number;
|
||||||
|
|
||||||
@ViewChild(CdkVirtualScrollViewport) private _scrollBar: CdkVirtualScrollViewport;
|
@ViewChild(CdkVirtualScrollViewport) scrollBar: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
||||||
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
||||||
@ -146,7 +147,7 @@ export class ProjectListingScreenComponent
|
|||||||
)
|
)
|
||||||
.subscribe((event) => {
|
.subscribe((event) => {
|
||||||
if (event instanceof NavigationStart && event.url !== '/main/projects') {
|
if (event instanceof NavigationStart && event.url !== '/main/projects') {
|
||||||
this._lastScrollPosition = this._scrollBar.measureScrollOffset('top');
|
this._lastScrollPosition = this.scrollBar.measureScrollOffset('top');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -155,7 +156,7 @@ export class ProjectListingScreenComponent
|
|||||||
this._appStateService.reset();
|
this._appStateService.reset();
|
||||||
this._loadEntitiesFromState();
|
this._loadEntitiesFromState();
|
||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
this._scrollBar.scrollTo({ top: this._lastScrollPosition });
|
this.scrollBar.scrollTo({ top: this._lastScrollPosition });
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDetach() {
|
ngOnDetach() {
|
||||||
|
|||||||
@ -184,7 +184,7 @@
|
|||||||
type="no-match"
|
type="no-match"
|
||||||
></redaction-empty-state>
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="itemSize" redactionHasScrollbar>
|
||||||
<div
|
<div
|
||||||
*cdkVirtualFor="
|
*cdkVirtualFor="
|
||||||
let fileStatus of displayedEntities
|
let fileStatus of displayedEntities
|
||||||
@ -309,6 +309,12 @@
|
|||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
</cdk-virtual-scroll-viewport>
|
</cdk-virtual-scroll-viewport>
|
||||||
|
|
||||||
|
<redaction-scroll-button
|
||||||
|
[scrollViewport]="scrollBar"
|
||||||
|
[itemSize]="itemSize"
|
||||||
|
*ngIf="scrollBar && itemSize > 0"
|
||||||
|
></redaction-scroll-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
|
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
|
||||||
|
|||||||
@ -48,6 +48,7 @@ export class ProjectOverviewScreenComponent
|
|||||||
needsWorkFilters: FilterModel[];
|
needsWorkFilters: FilterModel[];
|
||||||
statusFilters: FilterModel[];
|
statusFilters: FilterModel[];
|
||||||
} = { needsWorkFilters: [], statusFilters: [] };
|
} = { needsWorkFilters: [], statusFilters: [] };
|
||||||
|
readonly itemSize = 80;
|
||||||
protected readonly _searchKey = 'searchField';
|
protected readonly _searchKey = 'searchField';
|
||||||
protected readonly _selectionKey = 'fileId';
|
protected readonly _selectionKey = 'fileId';
|
||||||
protected readonly _sortKey = 'project-overview';
|
protected readonly _sortKey = 'project-overview';
|
||||||
@ -59,7 +60,7 @@ export class ProjectOverviewScreenComponent
|
|||||||
private _lastScrollPosition: number;
|
private _lastScrollPosition: number;
|
||||||
private _lastOpenedFileId = '';
|
private _lastOpenedFileId = '';
|
||||||
|
|
||||||
@ViewChild(CdkVirtualScrollViewport) private _scrollBar: CdkVirtualScrollViewport;
|
@ViewChild(CdkVirtualScrollViewport) scrollBar: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
@ViewChild('statusFilter') private _statusFilterComponent: FilterComponent;
|
||||||
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
@ViewChild('peopleFilter') private _peopleFilterComponent: FilterComponent;
|
||||||
@ -147,7 +148,7 @@ export class ProjectOverviewScreenComponent
|
|||||||
.pipe(filter((events) => events instanceof NavigationStart))
|
.pipe(filter((events) => events instanceof NavigationStart))
|
||||||
.subscribe((event: NavigationStart) => {
|
.subscribe((event: NavigationStart) => {
|
||||||
if (!event.url.endsWith(this._appStateService.activeProjectId)) {
|
if (!event.url.endsWith(this._appStateService.activeProjectId)) {
|
||||||
this._lastScrollPosition = this._scrollBar.measureScrollOffset('top');
|
this._lastScrollPosition = this.scrollBar.measureScrollOffset('top');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -162,7 +163,7 @@ export class ProjectOverviewScreenComponent
|
|||||||
ngOnAttach() {
|
ngOnAttach() {
|
||||||
this._loadEntitiesFromState();
|
this._loadEntitiesFromState();
|
||||||
this.ngOnInit();
|
this.ngOnInit();
|
||||||
this._scrollBar.scrollTo({ top: this._lastScrollPosition });
|
this.scrollBar.scrollTo({ top: this._lastScrollPosition });
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDetach() {
|
ngOnDetach() {
|
||||||
|
|||||||
18
apps/red-ui/src/assets/icons/general/arrow-down-o.svg
Normal file
18
apps/red-ui/src/assets/icons/general/arrow-down-o.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M14.8285 12.0259L16.2427 13.4402L12 17.6828L7.7574 13.4402L9.17161 12.0259L11 13.8544V6.31724H13V13.8544L14.8285 12.0259Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M19.7782 19.7782C15.4824 24.0739 8.51759 24.0739 4.22183 19.7782C-0.0739417 15.4824 -0.0739417 8.51759 4.22183 4.22183C8.51759 -0.0739419 15.4824 -0.0739419 19.7782 4.22183C24.0739 8.51759 24.0739 15.4824 19.7782 19.7782ZM18.364 18.364C14.8492 21.8787 9.15076 21.8787 5.63604 18.364C2.12132 14.8492 2.12132 9.15076 5.63604 5.63604C9.15076 2.12132 14.8492 2.12132 18.364 5.63604C21.8787 9.15076 21.8787 14.8492 18.364 18.364Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 841 B |
Loading…
x
Reference in New Issue
Block a user