Pull request #132: Quick nav scroll to top/bottom

Merge in RED/ui from RED-897 to master

* commit 'a4b3bd498d536bded564e7438531d64b453cf110':
  Fixed enable/disable quick nav buttons
  Remove logic from getter
  Quick nav scroll to top/bottom
This commit is contained in:
Timo Bejan 2021-03-22 08:32:12 +01:00
commit 11a2f5adb0
9 changed files with 140 additions and 21 deletions

View File

@ -44,6 +44,8 @@ export class IconsModule {
'lightning',
'logout',
'menu',
'nav-first',
'nav-last',
'needs-work',
'new-tab',
'notification',

View File

@ -217,17 +217,37 @@
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="pagesPanelActive"
class="pages"
class="quick-navigation"
tabindex="0"
>
<redaction-page-indicator
(pageSelected)="pageSelectedByClick($event)"
*ngFor="let pageNumber of displayedPages"
[active]="pageNumber === activeViewerPage"
[number]="pageNumber"
[viewedPages]="fileData.viewedPages"
<div
class="jump"
[class.disabled]="!quickScrollFirstEnabled"
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
matTooltipPosition="above"
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
>
</redaction-page-indicator>
<mat-icon svgIcon="red:nav-first"></mat-icon>
</div>
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
<redaction-page-indicator
(pageSelected)="pageSelectedByClick($event)"
*ngFor="let pageNumber of displayedPages"
[active]="pageNumber === activeViewerPage"
[number]="pageNumber"
[viewedPages]="fileData.viewedPages"
>
</redaction-page-indicator>
</div>
<div
class="jump"
[class.disabled]="!quickScrollLastEnabled"
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
matTooltipPosition="above"
(click)="scrollQuickNavLast()"
>
<mat-icon svgIcon="red:nav-last"></mat-icon>
</div>
</div>
<div style="overflow: hidden; width: 100%;">

View File

@ -58,7 +58,7 @@
box-sizing: border-box;
display: flex;
.pages,
.quick-navigation,
.annotations {
overflow-y: scroll;
outline: none;
@ -68,10 +68,44 @@
}
}
.pages {
.quick-navigation {
border-right: 1px solid $separator;
min-width: 61px;
@include no-scroll-bar();
overflow: hidden;
display: flex;
flex-direction: column;
.jump {
min-height: 32px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.25s;
&:not(.disabled):hover {
background-color: $grey-6;
}
mat-icon {
width: 16px;
height: 16px;
}
&.disabled {
cursor: default;
mat-icon {
opacity: 0.3;
}
}
}
.pages {
@include no-scroll-bar();
overflow: auto;
flex: 1;
}
}
.page-separator {

View File

@ -29,7 +29,6 @@ import { FormBuilder, FormGroup } from '@angular/forms';
import { FileManagementControllerService, StatusControllerService } from '@redaction/red-ui-http';
import { PdfViewerDataService } from '../service/pdf-viewer-data.service';
import { download } from '../../../utils/file-download-utils';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { ViewMode } from '../model/view-mode';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
@ -81,10 +80,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
return this._instance;
}
get displayedPages(): number[] {
return Object.keys(this.displayedAnnotations).map((key) => Number(key));
}
get activeViewerPage() {
return this._instance?.docViewer?.getCurrentPage();
}
@ -119,6 +114,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
public analysisProgress: number;
public analysisInterval: number;
public quickScrollFirstEnabled = false;
public quickScrollLastEnabled = false;
public displayedPages: number[] = [];
get indeterminateMode() {
return (
this.analysisProgress > 100 || this.appStateService.activeFile.analysisDuration < 3 * 1000 // it takes longer than usual - switch to indeterminate
@ -342,11 +342,31 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
this._scrollToFirstElement(elements);
}
private _scrollQuickNavigation() {
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${this.activeViewerPage}`);
private _scrollQuickNavigationToPage(page: number) {
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`);
this._scrollToFirstElement(elements);
}
private _scrollQuickNavigation() {
let quickNavPageIndex = this.displayedPages.findIndex((p) => p >= this.activeViewerPage);
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== this.activeViewerPage) {
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
}
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
}
public scrollQuickNavFirst() {
if (this.displayedPages.length > 0) {
this._scrollQuickNavigationToPage(this.displayedPages[0]);
}
}
public scrollQuickNavLast() {
if (this.displayedPages.length > 0) {
this._scrollQuickNavigationToPage(this.displayedPages[this.displayedPages.length - 1]);
}
}
private _scrollAnnotations() {
if (this.firstSelectedAnnotation?.pageNumber === this.activeViewerPage) {
return;
@ -570,6 +590,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
filtersChanged(filters: FilterModel[]) {
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(this.annotations, filters);
this.displayedPages = Object.keys(this.displayedAnnotations).map((key) => Number(key));
this.computeQuickNavButtonsState();
this._changeDetectorRef.markForCheck();
}
@ -579,6 +601,13 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
}
}
public computeQuickNavButtonsState() {
const element: HTMLElement = this._quickNavigationElement.nativeElement.querySelector(`#pages`);
const { scrollTop, scrollHeight, clientHeight } = element;
this.quickScrollFirstEnabled = scrollTop !== 0;
this.quickScrollLastEnabled = scrollHeight !== scrollTop + clientHeight;
}
private _cleanupAndRedrawManualAnnotations() {
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
this.fileData.manualRedactions = manualRedactions;

View File

@ -213,7 +213,7 @@
</div>
<div *ngIf="!fileStatus.isError">
<div class="pages">
<div class="quick-navigation">
<mat-icon svgIcon="red:pages"></mat-icon>
{{ fileStatus.numberOfPages }}
</div>

View File

@ -41,7 +41,7 @@ cdk-virtual-scroll-viewport {
max-width: 25vw;
}
.pages {
.quick-navigation {
display: flex;
flex-direction: row;
align-items: center;

View File

@ -306,7 +306,11 @@
"new-tab-ssr": "Open Document in Server Side Rendering Mode",
"html-debug": "Open Document HTML Debug",
"download-original-file": "Download Original File",
"exit-fullscreen": "Exit Full Screen (F)"
"exit-fullscreen": "Exit Full Screen (F)",
"quick-nav": {
"jump-first": "Jump to first annotation",
"jump-last": "Jump to last annotation"
}
},
"annotation-actions": {
"message": {

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ED22EA6D-C390-4471-B1E4-6B17BA55D5F9</title>
<g id="Annotations-Jump-to-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="03.Jump-to-first-annotation" transform="translate(-1109.000000, -181.000000)">
<rect x="0" y="0" width="1440" height="750"></rect>
<rect id="Rectangle" x="1087" y="173" width="60" height="32"></rect>
<g id="right_white" transform="translate(1109.000000, 181.000000)" fill="currentColor" fill-rule="nonzero">
<polygon id="Path" transform="translate(8.000000, 7.680000) scale(1, -1) translate(-8.000000, -7.680000) " points="12 6.24 10.88 5.12 8 8 7.2 7.2 5.12 5.12 4 6.24 8 10.24"></polygon>
<polygon id="Path" transform="translate(8.000000, 11.840000) scale(1, -1) translate(-8.000000, -11.840000) " points="12 10.4 10.88 9.28 8 12.16 7.2 11.36 5.12 9.28 4 10.4 8 14.4"></polygon>
<polygon id="Path" transform="translate(8.000000, 2.720000) scale(1, -1) translate(-8.000000, -2.720000) " points="14.4 1.92 1.6 1.92 1.22124533e-14 1.92 1.22124533e-14 3.52 16 3.52 16 1.92"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>F189455C-659F-4467-9C15-B17B152A29C5</title>
<g id="Annotations-Jump-to-" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="03.Jump-to-first-annotation" transform="translate(-1109.000000, -726.000000)">
<rect x="0" y="0" width="1440" height="750"></rect>
<rect id="Rectangle" x="1087" y="718" width="60" height="32"></rect>
<g id="right_white" transform="translate(1117.000000, 734.000000) scale(1, -1) translate(-1117.000000, -734.000000) translate(1109.000000, 726.000000)" fill="currentColor" fill-rule="nonzero">
<polygon id="Path" transform="translate(8.000000, 7.680000) scale(1, -1) translate(-8.000000, -7.680000) " points="12 6.24 10.88 5.12 8 8 7.2 7.2 5.12 5.12 4 6.24 8 10.24"></polygon>
<polygon id="Path" transform="translate(8.000000, 11.840000) scale(1, -1) translate(-8.000000, -11.840000) " points="12 10.4 10.88 9.28 8 12.16 7.2 11.36 5.12 9.28 4 10.4 8 14.4"></polygon>
<polygon id="Path" transform="translate(8.000000, 2.720000) scale(1, -1) translate(-8.000000, -2.720000) " points="14.4 1.92 1.6 1.92 1.22124533e-14 1.92 1.22124533e-14 3.52 16 3.52 16 1.92"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB