From d8e9bce41d5d88512b5c2d929617f6afcfa89372 Mon Sep 17 00:00:00 2001 From: Timo Bejan Date: Wed, 14 Oct 2020 11:42:28 +0300 Subject: [PATCH] scroll into view --- apps/red-ui/src/app/icons/icons.module.ts | 14 +++ .../file-preview-screen.component.html | 28 +++-- .../file-preview-screen.component.ts | 106 ++++++++++++++++-- .../file/pdf-viewer/pdf-viewer.component.ts | 8 +- .../file/service/viewer-sync.service.ts | 14 ++- .../src/assets/icons/general/bar_chart.svg | 50 +++++++++ .../red-ui/src/assets/icons/general/pages.svg | 63 +++++++++++ .../src/assets/styles/red-page-layout.scss | 6 +- 8 files changed, 264 insertions(+), 25 deletions(-) create mode 100644 apps/red-ui/src/assets/icons/general/bar_chart.svg create mode 100644 apps/red-ui/src/assets/icons/general/pages.svg diff --git a/apps/red-ui/src/app/icons/icons.module.ts b/apps/red-ui/src/app/icons/icons.module.ts index 90e9f3010..a4567806a 100644 --- a/apps/red-ui/src/app/icons/icons.module.ts +++ b/apps/red-ui/src/app/icons/icons.module.ts @@ -137,6 +137,20 @@ export class IconsModule { '/assets/icons/general/secret.svg' ) ); + iconRegistry.addSvgIconInNamespace( + 'red', + 'pages', + sanitizer.bypassSecurityTrustResourceUrl( + '/assets/icons/general/pages.svg' + ) + ); + iconRegistry.addSvgIconInNamespace( + 'red', + 'bar-chart', + sanitizer.bypassSecurityTrustResourceUrl( + '/assets/icons/general/bar_chart.svg' + ) + ); } diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html index 8272cc69d..f91642ffa 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html @@ -21,10 +21,12 @@ @@ -32,15 +34,16 @@
-
+
Quick Navigation
-
+
-
+
Annotations
-
+
{{annotation.Id}}
@@ -89,8 +93,12 @@ [config]="[{ length: 1, label: 'Unassigned', color: 'unassigned'}]">
-
645
-
9
+
+ + {{appStateService.activeFile.numberOfPages}}
+
+ + {{annotations.length}}
@@ -102,21 +110,21 @@ Added on
- 22 Sep. 2020, 12:15 PM + {{appStateService.activeFile.added | date:'medium'}}
Added by
- Timo Bejan + {{user.name}}
Last modified on
- 22 Sep. 2020, 12:15 PM + {{appStateService.activeFile.lastUpdated | date:'medium'}}
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts index a47da0ef4..16c85ef7b 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts @@ -16,6 +16,7 @@ import {Annotations} from '@pdftron/webviewer'; import {PdfViewerComponent} from '../pdf-viewer/pdf-viewer.component'; import {AnnotationUtils} from '../../../utils/annotation-utils'; import {ManualRedactionDialogComponent} from "../manual-redaction-dialog/manual-redaction-dialog.component"; +import {UserService} from "../../../user/user.service"; @Component({ @@ -35,14 +36,18 @@ export class FilePreviewScreenComponent implements OnInit { @ViewChild('annotationsContainer') private _annotationsContainer: ElementRef; + @ViewChild('navigationTabElement') + private _navigationTabElement: ElementRef; + public fileId: string; public annotations: Annotations.Annotation[] = []; public selectedAnnotation: Annotations.Annotation; - public selectedPageNumber: number; public quickNavigation: { pageNumber: number, redactions: number, hints: number }[] = []; private _manualRedactionEntry: ManualRedactionEntry; + activeViewerPage: number; + constructor( public readonly appStateService: AppStateService, private readonly _changeDetectorRef: ChangeDetectorRef, @@ -53,6 +58,7 @@ export class FilePreviewScreenComponent implements OnInit { private readonly _viewerSyncService: ViewerSyncService, private readonly _dialog: MatDialog, private readonly _router: Router, + private readonly _userService: UserService, private readonly _fileUploadControllerService: FileUploadControllerService, private readonly _projectControllerService: ProjectControllerService) { this._activatedRoute.params.subscribe(params => { @@ -62,6 +68,10 @@ export class FilePreviewScreenComponent implements OnInit { }); } + get user() { + return this._userService.user; + } + public ngOnInit(): void { // PDFTRON cache fix localStorage.clear(); @@ -96,6 +106,9 @@ export class FilePreviewScreenComponent implements OnInit { public selectTab(value: 'ANNOTATIONS' | 'INFO' | 'NAVIGATION') { this._selectedTab = value; + setTimeout(() => { + this._scrollViews(); + }, 50); } public handleAnnotationsAdded(annotations: Annotations.Annotation[]) { @@ -123,6 +136,7 @@ export class FilePreviewScreenComponent implements OnInit { public handleAnnotationSelected(annotation: Annotations.Annotation) { this.selectedAnnotation = annotation; this.scrollToAnnotation(annotation); + this._changeDetectorRef.detectChanges(); } public selectAnnotation(annotation: Annotations.Annotation) { @@ -144,13 +158,7 @@ export class FilePreviewScreenComponent implements OnInit { return; } - const {top, height} = el.getBoundingClientRect(); - const headerHeight = window.innerHeight - this._annotationsContainer.nativeElement.getBoundingClientRect().height; - - if (top < headerHeight || top > window.innerHeight - height - 30) { - const scrollTop = this._annotationsContainer.nativeElement.scrollTop - 30; - this._annotationsContainer.nativeElement.scroll({top: scrollTop + top - headerHeight, behavior: 'smooth'}); - } + el.scrollIntoView({block: 'center', inline: 'center', behavior: 'smooth'}); } public get navigationTab() { @@ -166,7 +174,6 @@ export class FilePreviewScreenComponent implements OnInit { } public selectPage(pageNumber: number) { - this.selectedPageNumber = pageNumber; this._viewerComponent.navigateToPage(pageNumber); } @@ -186,4 +193,85 @@ export class FilePreviewScreenComponent implements OnInit { this._manualRedactionEntry = null; }) } + + viewerPageChanged(pageNumber: number) { + if (Number.isInteger(pageNumber)) { + this.activeViewerPage = this._viewerSyncService.activeViewerPage; + this._scrollViews(); + } + this._changeDetectorRef.detectChanges(); + } + + private _scrollViews() { + this._scrollQuickNavigation(); + this._scrollAnnotations(); + } + + private _scrollQuickNavigation() { + const elements: any[] = this._navigationTabElement.nativeElement.querySelectorAll(`#quick-nav-page-${this.activeViewerPage}`); + if (elements.length > 0) { + elements[0].scrollIntoViewIfNeeded(); + } + } + + private _scrollAnnotations() { + const elements: any[] = this._annotationsContainer.nativeElement.querySelectorAll(`div[annotation-page="${this.activeViewerPage}"]`); + if (elements.length > 0) { + elements[0].scrollIntoViewIfNeeded(); + } + } + } + + +(function() { + // @ts-ignore + if (!Element.prototype.scrollIntoViewIfNeeded) { + // @ts-ignore + Element.prototype.scrollIntoViewIfNeeded = function(centerIfNeeded) { + centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded; + + const parent = this.parentNode, + parentComputedStyle = window.getComputedStyle(parent, null), + parentBorderTopWidth = parseInt( + parentComputedStyle.getPropertyValue('border-top-width'), + 10 + ), + parentBorderLeftWidth = parseInt( + parentComputedStyle.getPropertyValue('border-left-width'), + 10 + ), + overTop = this.offsetTop - parent.offsetTop < parent.scrollTop, + overBottom = + this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth > + parent.scrollTop + parent.clientHeight, + overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft, + overRight = + this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth > + parent.scrollLeft + parent.clientWidth, + alignWithTop = overTop && !overBottom; + + if ((overTop || overBottom) && centerIfNeeded) { + parent.scrollTop = + this.offsetTop - + parent.offsetTop - + parent.clientHeight / 2 - + parentBorderTopWidth + + this.clientHeight / 2; + } + + if ((overLeft || overRight) && centerIfNeeded) { + parent.scrollLeft = + this.offsetLeft - + parent.offsetLeft - + parent.clientWidth / 2 - + parentBorderLeftWidth + + this.clientWidth / 2; + } + + if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) { + this.scrollIntoView(alignWithTop); + } + }; + } +})(); diff --git a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts index 991258116..d0a769648 100644 --- a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts @@ -17,7 +17,6 @@ import WebViewer, {Annotations, WebViewerInstance} from '@pdftron/webviewer'; import {TranslateService} from '@ngx-translate/core'; import {ViewerSyncService} from '../service/viewer-sync.service'; import {MatDialog} from "@angular/material/dialog"; -import {ManualRedactionDialogComponent} from "../manual-redaction-dialog/manual-redaction-dialog.component"; export enum FileType { ORIGINAL = 'ORIGINAL', @@ -39,6 +38,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { @Output() annotationsAdded = new EventEmitter(); @Output() annotationSelected = new EventEmitter(); @Output() manualAnnotationRequested = new EventEmitter(); + @Output() pageChanged = new EventEmitter(); @ViewChild('viewer', {static: true}) viewer: ElementRef; wvInstance: WebViewerInstance; @@ -95,6 +95,10 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { } })); + instance.docViewer.on('pageComplete', (p) => { + this.pageChanged.emit(p); + }); + instance.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler); instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'file.pdf'}); }); @@ -152,7 +156,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { const entry: ManualRedactionEntry = {positions: []}; for (const key of Object.keys(selectedQuads)) { for (const quad of selectedQuads[key]) { - entry.positions.push(this.toPosition(parseInt(key,10), quad)); + entry.positions.push(this.toPosition(parseInt(key, 10), quad)); } } entry.value = text; diff --git a/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts b/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts index 548b14ca6..e3dc8887d 100644 --- a/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts +++ b/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts @@ -1,5 +1,5 @@ -import { Injectable } from '@angular/core'; -import { WebViewerInstance } from '@pdftron/webviewer'; +import {Injectable} from '@angular/core'; +import {WebViewerInstance} from '@pdftron/webviewer'; @Injectable({ providedIn: 'root' @@ -13,6 +13,14 @@ export class ViewerSyncService { constructor() { } + get activeViewerPage() { + if (this._activeViewer) { + const lastActiveViewer = this._viewers[this._activeViewer]; + return lastActiveViewer.docViewer.getCurrentPage(); + } + return 1; + } + syncViewers() { localStorage.clear(); if (this._activeViewer) { @@ -36,7 +44,7 @@ export class ViewerSyncService { // sync layout and display mode instance.docViewer.setCurrentPage(lastPageNumber); instance.setLayoutMode(lastLayoutMode); - const displayMode =instance.docViewer.getDisplayModeManager().getDisplayMode(); + const displayMode = instance.docViewer.getDisplayModeManager().getDisplayMode(); displayMode.mode = lastDisplayMode; instance.docViewer.getDisplayModeManager().setDisplayMode(displayMode); // Synchronize zoom - needs to be done before scrolling diff --git a/apps/red-ui/src/assets/icons/general/bar_chart.svg b/apps/red-ui/src/assets/icons/general/bar_chart.svg new file mode 100644 index 000000000..41cecdd3b --- /dev/null +++ b/apps/red-ui/src/assets/icons/general/bar_chart.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/red-ui/src/assets/icons/general/pages.svg b/apps/red-ui/src/assets/icons/general/pages.svg new file mode 100644 index 000000000..300a88d70 --- /dev/null +++ b/apps/red-ui/src/assets/icons/general/pages.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/red-ui/src/assets/styles/red-page-layout.scss b/apps/red-ui/src/assets/styles/red-page-layout.scss index caa588b50..f7dc22bf8 100644 --- a/apps/red-ui/src/assets/styles/red-page-layout.scss +++ b/apps/red-ui/src/assets/styles/red-page-layout.scss @@ -188,6 +188,10 @@ html, body { display: none !important; } -.pointer{ +.not-visible { + visibility: hidden; +} + +.pointer { cursor: pointer; }