Pull request #6: Annotations
Merge in RED/ui from annotations to master * commit 'f7bcdf8bc6bb8a8e41a135633bc73e861ff6176f': Sort to selected annotation in sidebar + fixes Sort annotations Select annotation
This commit is contained in:
commit
936fece85d
@ -21,7 +21,9 @@
|
|||||||
<div class="left-container">
|
<div class="left-container">
|
||||||
<redaction-pdf-viewer [class.visible]="activeViewer === 'ANNOTATED'" [fileId]="fileId" fileType="ANNOTATED"
|
<redaction-pdf-viewer [class.visible]="activeViewer === 'ANNOTATED'" [fileId]="fileId" fileType="ANNOTATED"
|
||||||
[fileStatus]="appStateService.activeFile"
|
[fileStatus]="appStateService.activeFile"
|
||||||
(fileReady)="fileReady('ANNOTATED')" (annotationsAdded)="handleAnnotationsAdded($event)"></redaction-pdf-viewer>
|
(fileReady)="fileReady('ANNOTATED')"
|
||||||
|
(annotationSelected)="handleAnnotationSelected($event)"
|
||||||
|
(annotationsAdded)="handleAnnotationsAdded($event)"></redaction-pdf-viewer>
|
||||||
<redaction-pdf-viewer [class.visible]="activeViewer === 'REDACTED'" [fileId]="fileId" fileType="REDACTED"
|
<redaction-pdf-viewer [class.visible]="activeViewer === 'REDACTED'" [fileId]="fileId" fileType="REDACTED"
|
||||||
(fileReady)="fileReady('REDACTED')"></redaction-pdf-viewer>
|
(fileReady)="fileReady('REDACTED')"></redaction-pdf-viewer>
|
||||||
</div>
|
</div>
|
||||||
@ -46,9 +48,15 @@
|
|||||||
Info
|
Info
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content" *ngIf="selectedTab === 'ANNOTATIONS'">
|
<div class="tab-content" #annotationsContainer *ngIf="selectedTab === 'ANNOTATIONS'">
|
||||||
<div *ngFor="let annotation of annotations">
|
<div *ngFor="let annotation of annotations"
|
||||||
{{annotation.Id+ ' '+annotation.getPageNumber() + ' content: ' + annotation.getContents() + 'status: '+annotation.getStatus()}}
|
class="annotation" [id]="'ann-' + annotation.Id"
|
||||||
|
(click)="selectAnnotation(annotation)"
|
||||||
|
[ngClass]="{ active: selectedAnnotation === annotation }">
|
||||||
|
<div>{{annotation.Id}}</div>
|
||||||
|
<div>Page {{annotation.getPageNumber()}}</div>
|
||||||
|
<div>{{annotation.getContents()}}</div>
|
||||||
|
<div>{{annotation.getStatus()}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content" *ngIf="selectedTab === 'INFO'">
|
<div class="tab-content" *ngIf="selectedTab === 'INFO'">
|
||||||
|
|||||||
@ -17,6 +17,8 @@ redaction-pdf-viewer {
|
|||||||
|
|
||||||
.tabs-title-row {
|
.tabs-title-row {
|
||||||
border-bottom: 1px solid rgba(226,228,233,0.9);
|
border-bottom: 1px solid rgba(226,228,233,0.9);
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 45px;
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@ -40,8 +42,12 @@ redaction-pdf-viewer {
|
|||||||
.actions-row {
|
.actions-row {
|
||||||
margin: $right-container-padding $right-container-padding 0;
|
margin: $right-container-padding $right-container-padding 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
padding: $right-container-padding;
|
padding: $right-container-padding;
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
height: calc(100vh - 110px - 40px - 45px - 3*#{$right-container-padding});
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-subtitle {
|
.stats-subtitle {
|
||||||
@ -55,4 +61,15 @@ redaction-pdf-viewer {
|
|||||||
|
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotation {
|
||||||
|
border: 1px solid $grey-2;
|
||||||
|
padding: 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-left: 2px solid $red-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import {ChangeDetectorRef, Component, OnInit} from '@angular/core';
|
import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import {FileUploadControllerService, ProjectControllerService, StatusControllerService} from '@redaction/red-ui-http';
|
import { FileUploadControllerService, ProjectControllerService, StatusControllerService } from '@redaction/red-ui-http';
|
||||||
import {TranslateService} from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import {NotificationService} from '../../../notification/notification.service';
|
import { NotificationService } from '../../../notification/notification.service';
|
||||||
import {MatDialog} from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import {AppStateService} from '../../../state/app-state.service';
|
import { AppStateService } from '../../../state/app-state.service';
|
||||||
import {FileDetailsDialogComponent} from './file-details-dialog/file-details-dialog.component';
|
import { FileDetailsDialogComponent } from './file-details-dialog/file-details-dialog.component';
|
||||||
import {ViewerSyncService} from '../service/viwer-sync.service';
|
import { ViewerSyncService } from '../service/viwer-sync.service';
|
||||||
import {Annotations} from "@pdftron/webviewer";
|
import { Annotations } from '@pdftron/webviewer';
|
||||||
|
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
|
||||||
|
import { AnnotationUtils } from '../../../utils/annotation-utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-file-preview-screen',
|
selector: 'redaction-file-preview-screen',
|
||||||
@ -15,12 +17,20 @@ import {Annotations} from "@pdftron/webviewer";
|
|||||||
styleUrls: ['./file-preview-screen.component.scss']
|
styleUrls: ['./file-preview-screen.component.scss']
|
||||||
})
|
})
|
||||||
export class FilePreviewScreenComponent implements OnInit {
|
export class FilePreviewScreenComponent implements OnInit {
|
||||||
|
|
||||||
projectId: string;
|
|
||||||
fileId: string;
|
|
||||||
annotations: Annotations.Annotation[] = [];
|
|
||||||
public selectedTab: 'ANNOTATIONS' | 'INFO' = 'ANNOTATIONS';
|
|
||||||
private _readyViewers: string[] = [];
|
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(
|
constructor(
|
||||||
public readonly appStateService: AppStateService,
|
public readonly appStateService: AppStateService,
|
||||||
@ -77,10 +87,44 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
|
|
||||||
handleAnnotationsAdded(annotations: Annotations.Annotation[]) {
|
handleAnnotationsAdded(annotations: Annotations.Annotation[]) {
|
||||||
this._changeDetectorRef.detectChanges();
|
this._changeDetectorRef.detectChanges();
|
||||||
for(let annotation of annotations){
|
for (const annotation of annotations) {
|
||||||
if(annotation.Id.indexOf(':')>=0){
|
if (annotation.Id.indexOf(':') >= 0) {
|
||||||
this.annotations.push(annotation);
|
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' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {tap} from "rxjs/operators";
|
|||||||
import WebViewer, {Annotations, WebViewerInstance} from "@pdftron/webviewer";
|
import WebViewer, {Annotations, WebViewerInstance} from "@pdftron/webviewer";
|
||||||
import {TranslateService} from "@ngx-translate/core";
|
import {TranslateService} from "@ngx-translate/core";
|
||||||
import {ViewerSyncService} from "../service/viwer-sync.service";
|
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;
|
@Input() fileStatus: FileStatus;
|
||||||
@Output() fileReady = new EventEmitter();
|
@Output() fileReady = new EventEmitter();
|
||||||
@Output() annotationsAdded = new EventEmitter<Annotations.Annotation[]>();
|
@Output() annotationsAdded = new EventEmitter<Annotations.Annotation[]>();
|
||||||
|
@Output() annotationSelected = new EventEmitter<Annotations.Annotation>();
|
||||||
|
|
||||||
@ViewChild('viewer', {static: true}) viewer: ElementRef;
|
@ViewChild('viewer', {static: true}) viewer: ElementRef;
|
||||||
wvInstance: WebViewerInstance;
|
wvInstance: WebViewerInstance;
|
||||||
@ -81,12 +83,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this._viewerSyncService.registerViewer(this.fileType, this.wvInstance);
|
this._viewerSyncService.registerViewer(this.fileType, this.wvInstance);
|
||||||
this._configureTextPopup();
|
this._configureTextPopup();
|
||||||
this._configureHeader();
|
this._configureHeader();
|
||||||
instance.annotManager.on('annotationChanged', (annotations,b,c) => {
|
instance.annotManager.on('annotationChanged', (annotations, action) => {
|
||||||
if(b === 'add'){
|
if(action === 'add'){
|
||||||
this.annotationsAdded.emit(annotations);
|
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.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler)
|
||||||
instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'file.pdf'});
|
instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'file.pdf'});
|
||||||
});
|
});
|
||||||
@ -123,8 +133,8 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
'textSquigglyToolButton',
|
'textSquigglyToolButton',
|
||||||
'textStrikeoutToolButton',
|
'textStrikeoutToolButton',
|
||||||
'linkButton',
|
'linkButton',
|
||||||
// 'toggleNotesButton',
|
'toggleNotesButton',
|
||||||
// 'notesPanel'
|
'notesPanel'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.wvInstance.textPopup.add(<any>{
|
this.wvInstance.textPopup.add(<any>{
|
||||||
@ -147,5 +157,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
this._viewerSyncService.deregisterInstance(this.fileType);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
apps/red-ui/src/app/utils/annotation-utils.ts
Normal file
18
apps/red-ui/src/app/utils/annotation-utils.ts
Normal file
@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,6 +38,7 @@ html, body {
|
|||||||
|
|
||||||
.actions-row {
|
.actions-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
height: 40px;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user