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 7ae087858..89c25f203 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,7 +21,9 @@
+ (fileReady)="fileReady('ANNOTATED')" + (annotationSelected)="handleAnnotationSelected($event)" + (annotationsAdded)="handleAnnotationsAdded($event)">
@@ -46,9 +48,15 @@ Info -
-
- {{annotation.Id+ ' '+annotation.getPageNumber() + ' content: ' + annotation.getContents() + 'status: '+annotation.getStatus()}} +
+
+
{{annotation.Id}}
+
Page {{annotation.getPageNumber()}}
+
{{annotation.getContents()}}
+
{{annotation.getStatus()}}
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss index 4d3c0137c..8b1faef8f 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss @@ -17,6 +17,8 @@ redaction-pdf-viewer { .tabs-title-row { border-bottom: 1px solid rgba(226,228,233,0.9); + box-sizing: border-box; + height: 45px; .tab { font-size: 13px; @@ -40,8 +42,12 @@ redaction-pdf-viewer { .actions-row { margin: $right-container-padding $right-container-padding 0; } + .tab-content { padding: $right-container-padding; + overflow-y: scroll; + overflow-x: hidden; + height: calc(100vh - 110px - 40px - 45px - 3*#{$right-container-padding}); } .stats-subtitle { @@ -55,4 +61,15 @@ redaction-pdf-viewer { margin-left: 12px; } + + .annotation { + border: 1px solid $grey-2; + padding: 14px; + font-size: 12px; + cursor: pointer; + + &.active { + border-left: 2px solid $red-1; + } + } } 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 00bc316ce..63d69cece 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 @@ -1,13 +1,15 @@ -import {ChangeDetectorRef, Component, OnInit} from '@angular/core'; -import {ActivatedRoute, Router} from '@angular/router'; -import {FileUploadControllerService, ProjectControllerService, StatusControllerService} from '@redaction/red-ui-http'; -import {TranslateService} from '@ngx-translate/core'; -import {NotificationService} from '../../../notification/notification.service'; -import {MatDialog} from '@angular/material/dialog'; -import {AppStateService} from '../../../state/app-state.service'; -import {FileDetailsDialogComponent} from './file-details-dialog/file-details-dialog.component'; -import {ViewerSyncService} from '../service/viwer-sync.service'; -import {Annotations} from "@pdftron/webviewer"; +import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { FileUploadControllerService, ProjectControllerService, StatusControllerService } from '@redaction/red-ui-http'; +import { TranslateService } from '@ngx-translate/core'; +import { NotificationService } from '../../../notification/notification.service'; +import { MatDialog } from '@angular/material/dialog'; +import { AppStateService } from '../../../state/app-state.service'; +import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component'; +import { ViewerSyncService } from '../service/viwer-sync.service'; +import { Annotations } from '@pdftron/webviewer'; +import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component'; +import { AnnotationUtils } from '../../../utils/annotation-utils'; @Component({ selector: 'redaction-file-preview-screen', @@ -15,12 +17,20 @@ import {Annotations} from "@pdftron/webviewer"; styleUrls: ['./file-preview-screen.component.scss'] }) export class FilePreviewScreenComponent implements OnInit { - - projectId: string; - fileId: string; - annotations: Annotations.Annotation[] = []; - public selectedTab: 'ANNOTATIONS' | 'INFO' = 'ANNOTATIONS'; private _readyViewers: string[] = []; + private _clickedAnnotationInSidebar = false; + private projectId: string; + + @ViewChild(PdfViewerComponent) + private _viewerComponent: PdfViewerComponent; + + @ViewChild('annotationsContainer') + private _annotationsContainer: ElementRef; + + public fileId: string; + public annotations: Annotations.Annotation[] = []; + public selectedTab: 'ANNOTATIONS' | 'INFO' = 'ANNOTATIONS'; + public selectedAnnotation: Annotations.Annotation; constructor( public readonly appStateService: AppStateService, @@ -77,10 +87,44 @@ export class FilePreviewScreenComponent implements OnInit { handleAnnotationsAdded(annotations: Annotations.Annotation[]) { this._changeDetectorRef.detectChanges(); - for(let annotation of annotations){ - if(annotation.Id.indexOf(':')>=0){ + for (const annotation of annotations) { + if (annotation.Id.indexOf(':') >= 0) { this.annotations.push(annotation); } } + this.annotations = AnnotationUtils.sortAnnotations(this.annotations); + } + + public handleAnnotationSelected(annotation: Annotations.Annotation) { + this.selectedAnnotation = annotation; + this.scrollToAnnotation(annotation); + } + + public selectAnnotation(annotation: Annotations.Annotation) { + this._clickedAnnotationInSidebar = true; + setTimeout(() => { + this._clickedAnnotationInSidebar = false; + }, 100); + this._viewerComponent.selectAnnotation(annotation); + } + + private scrollToAnnotation(annotation: Annotations.Annotation) { + if (!annotation || this._clickedAnnotationInSidebar) { + return; + } + const el = document.getElementById('ann-' + annotation.Id); + + if (!el) { + console.error(`Annotation with id ${annotation.Id} does not exist!`); + 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' }); + } } } 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 1894c09c8..bb24d6db8 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 @@ -16,6 +16,7 @@ import {tap} from "rxjs/operators"; import WebViewer, {Annotations, WebViewerInstance} from "@pdftron/webviewer"; import {TranslateService} from "@ngx-translate/core"; import {ViewerSyncService} from "../service/viwer-sync.service"; +import { AnnotationUtils } from '../../../utils/annotation-utils'; @@ -37,6 +38,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { @Input() fileStatus: FileStatus; @Output() fileReady = new EventEmitter(); @Output() annotationsAdded = new EventEmitter(); + @Output() annotationSelected = new EventEmitter(); @ViewChild('viewer', {static: true}) viewer: ElementRef; wvInstance: WebViewerInstance; @@ -81,12 +83,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { this._viewerSyncService.registerViewer(this.fileType, this.wvInstance); this._configureTextPopup(); this._configureHeader(); - instance.annotManager.on('annotationChanged', (annotations,b,c) => { - if(b === 'add'){ + instance.annotManager.on('annotationChanged', (annotations, action) => { + if(action === 'add'){ this.annotationsAdded.emit(annotations); } }); + instance.annotManager.on('annotationSelected', ((annotationList, action) => { + if (action === 'deselected') { + this.annotationSelected.emit(null); + } else { + this.annotationSelected.emit(annotationList[0]); + } + })); + instance.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler) instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'file.pdf'}); }); @@ -123,8 +133,8 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { 'textSquigglyToolButton', 'textStrikeoutToolButton', 'linkButton', - // 'toggleNotesButton', - // 'notesPanel' + 'toggleNotesButton', + 'notesPanel' ]); this.wvInstance.textPopup.add({ @@ -147,5 +157,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { this._viewerSyncService.deregisterInstance(this.fileType); } + public selectAnnotation(annotation: Annotations.Annotation) { + this.wvInstance.annotManager.deselectAllAnnotations(); + this.wvInstance.annotManager.selectAnnotation(annotation); + this.wvInstance.docViewer.displayPageLocation( + annotation.getPageNumber(), + 0, + annotation.getY() - 100); + } } diff --git a/apps/red-ui/src/app/utils/annotation-utils.ts b/apps/red-ui/src/app/utils/annotation-utils.ts new file mode 100644 index 000000000..d12a5bf59 --- /dev/null +++ b/apps/red-ui/src/app/utils/annotation-utils.ts @@ -0,0 +1,18 @@ +import { Annotations } from '@pdftron/webviewer'; + +export class AnnotationUtils { + public static sortAnnotations(annotations: Annotations.Annotation[]): Annotations.Annotation[] { + return annotations.sort((ann1, ann2) => { + if (ann1.getPageNumber() === ann2.getPageNumber()) { + if (ann1.getY() === ann2.getY()) { + if (ann1.getX() === ann2.getY()) { + return 0; + } + return ann1.getX() < ann2.getX() ? -1 : 1; + } + return ann1.getY() < ann2.getY() ? -1 : 1; + } + return ann1.getPageNumber() < ann2.getPageNumber() ? -1 : 1; + }); + } +} 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 e1d6ad22e..c3b586e7b 100644 --- a/apps/red-ui/src/assets/styles/red-page-layout.scss +++ b/apps/red-ui/src/assets/styles/red-page-layout.scss @@ -38,6 +38,7 @@ html, body { .actions-row { display: flex; + height: 40px; > div { padding: 10px;