scroll into view

This commit is contained in:
Timo Bejan 2020-10-14 11:42:28 +03:00
parent 8a52386477
commit d8e9bce41d
8 changed files with 264 additions and 25 deletions

View File

@ -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'
)
);
}

View File

@ -21,10 +21,12 @@
<redaction-pdf-viewer [class.visible]="activeViewer === 'ANNOTATED'" [fileId]="fileId" fileType="ANNOTATED"
[fileStatus]="appStateService.activeFile"
(fileReady)="fileReady('ANNOTATED')"
(pageChanged)="viewerPageChanged($event)"
(manualAnnotationRequested)="handleManualAnnotationRequest($event)"
(annotationSelected)="handleAnnotationSelected($event)"
(annotationsAdded)="handleAnnotationsAdded($event)"></redaction-pdf-viewer>
<redaction-pdf-viewer [class.visible]="activeViewer === 'REDACTED'" [fileId]="fileId" fileType="REDACTED"
(pageChanged)="viewerPageChanged($event)"
(manualAnnotationRequested)="handleManualAnnotationRequest($event)"
(fileReady)="fileReady('REDACTED')"></redaction-pdf-viewer>
</div>
@ -32,15 +34,16 @@
<div class="right-fixed-container">
<!-- Quick navigation tab-->
<div class="vertical" (click)="selectTab('NAVIGATION')" [ngClass]="{ active: navigationTab}">
<div class="vertical" (click)="selectTab('NAVIGATION')" [ngClass]="{ active: navigationTab}" #navigationTabElement>
<div class="tab-title" [ngClass]="navigationTab ? 'heading' : 'subheading'">
Quick Navigation
</div>
<div *ngIf="navigationTab" class="tab-content">
<div class="tab-content" [class.not-visible]="!navigationTab">
<div *ngFor="let item of quickNavigation | sortBy:'asc':'number'"
class="page-navigation"
[ngClass]="{ active: item.pageNumber === selectedPageNumber }"
[id]="'quick-nav-page-'+item.pageNumber"
[ngClass]="{ active: item.pageNumber === activeViewerPage }"
(click)="selectPage(item.pageNumber)"
>
<div class="page-number">Page {{ item.pageNumber }}</div>
@ -59,14 +62,15 @@
</div>
<!-- Annotations tab-->
<div (click)="selectTab('ANNOTATIONS')" class="vertical" [ngClass]="{ active: annotationsTab }">
<div (click)="selectTab('ANNOTATIONS')" class="vertical" [ngClass]="{ active: annotationsTab }" #annotationsContainer>
<div class="tab-title" [ngClass]="annotationsTab ? 'heading' : 'subheading'">
Annotations
</div>
<div *ngIf="annotationsTab" class="tab-content" #annotationsContainer>
<div class="tab-content" [class.not-visible]="!annotationsTab">
<div *ngFor="let annotation of annotations"
class="annotation" [id]="'ann-' + annotation.Id"
attr.annotation-page="{{annotation.getPageNumber()}}"
(click)="selectAnnotation(annotation)"
[ngClass]="{ active: selectedAnnotation === annotation }">
<div class="clamp-1">{{annotation.Id}}</div>
@ -89,8 +93,12 @@
[config]="[{ length: 1, label: 'Unassigned', color: 'unassigned'}]"></redaction-status-bar>
<div class="subtitle stats-subtitle mt-5">
<div>645</div>
<div>9</div>
<div>
<mat-icon svgIcon="red:pages"></mat-icon>
{{appStateService.activeFile.numberOfPages}}</div>
<div>
<mat-icon svgIcon="red:bar-chart"></mat-icon>
{{annotations.length}}</div>
</div>
<div class="flex-row mt-20">
@ -102,21 +110,21 @@
Added on
</div>
<div class="subtitle mt-5">
22 Sep. 2020, 12:15 PM
{{appStateService.activeFile.added | date:'medium'}}
</div>
<div class="subheading mt-20">
Added by
</div>
<div class="subtitle mt-5">
Timo Bejan
{{user.name}}
</div>
<div class="subheading mt-20">
Last modified on
</div>
<div class="subtitle mt-5">
22 Sep. 2020, 12:15 PM
{{appStateService.activeFile.lastUpdated | date:'medium'}}
</div>
</div>
</div>

View File

@ -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);
}
};
}
})();

View File

@ -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<Annotations.Annotation[]>();
@Output() annotationSelected = new EventEmitter<Annotations.Annotation>();
@Output() manualAnnotationRequested = new EventEmitter<ManualRedactionEntry>();
@Output() pageChanged = new EventEmitter<number>();
@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;

View File

@ -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

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<g>
<g>
<path d="M117.547,234.667H10.88c-5.888,0-10.667,4.779-10.667,10.667v256C0.213,507.221,4.992,512,10.88,512h106.667
c5.909,0,10.667-4.779,10.667-10.667v-256C128.213,239.445,123.435,234.667,117.547,234.667z M106.88,490.667H21.547V256h85.333
V490.667z"/>
<path d="M309.12,0H202.453c-5.888,0-10.667,4.779-10.667,10.667v490.667c0,5.888,4.779,10.667,10.667,10.667H309.12
c5.888,0,10.667-4.779,10.667-10.667V10.667C319.787,4.779,315.008,0,309.12,0z M298.453,490.667H213.12V21.333h85.333V490.667z"
/>
<path d="M501.12,106.667H394.453c-5.888,0-10.667,4.779-10.667,10.667v384c0,5.888,4.779,10.667,10.667,10.667H501.12
c5.888,0,10.667-4.779,10.667-10.667v-384C511.787,111.445,507.008,106.667,501.12,106.667z M490.453,490.667H405.12V128h85.333
V490.667z"/>
</g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="548.291px" height="548.291px" viewBox="0 0 548.291 548.291" style="enable-background:new 0 0 548.291 548.291;"
xml:space="preserve">
<g>
<path d="M189.2,293.611c-1.806-6.027-3.609-13.564-5.119-19.583h-0.296c-1.515,6.019-3.027,13.703-4.665,19.583l-6.037,21.554
h22.452L189.2,293.611z"/>
<path d="M486.201,196.124h-13.166V132.59c0-0.396-0.062-0.795-0.115-1.196c-0.021-2.523-0.825-5-2.552-6.963L364.657,3.677
c-0.033-0.031-0.064-0.042-0.085-0.073c-0.63-0.707-1.364-1.292-2.143-1.795c-0.229-0.157-0.461-0.286-0.702-0.421
c-0.672-0.366-1.387-0.671-2.121-0.892c-0.2-0.055-0.379-0.136-0.577-0.188C358.23,0.118,357.401,0,356.562,0H96.757
C84.894,0,75.256,9.651,75.256,21.502v174.613H62.092c-16.971,0-30.732,13.756-30.732,30.733v159.812
c0,16.968,13.761,30.731,30.732,30.731h13.164V526.79c0,11.854,9.638,21.501,21.501,21.501h354.776
c11.853,0,21.501-9.647,21.501-21.501V417.392h13.166c16.966,0,30.729-13.764,30.729-30.731V226.854
C516.93,209.872,503.167,196.124,486.201,196.124z M96.757,21.502h249.054v110.009c0,5.939,4.817,10.75,10.751,10.75h94.972v53.861
H96.757V21.502z M404.394,296.781v18.678h-37.361v24.104h41.733v18.846h-64.778v-101.55h62.674v18.835h-39.629v21.098h37.361
V296.781z M292.592,340.918c5.265,0,9.484-0.6,11.297-1.501v-21.25h-15.665v-17.933h37.816v53.502
c-7.081,2.404-20.492,5.724-33.9,5.724c-18.538,0-31.953-4.674-41.287-13.712c-9.347-8.736-14.475-21.996-14.321-36.926
c0.153-33.743,24.712-53.026,58.007-53.026c13.107,0,23.196,2.562,28.174,4.976l-4.819,18.381
c-5.575-2.415-12.494-4.373-23.648-4.373c-19.132,0-33.591,10.86-33.591,32.846C260.649,328.562,273.759,340.918,292.592,340.918z
M231.399,358.409H206.69l-7.848-26.068h-29.066l-7.239,26.068h-23.809l31.042-101.555h30.124L231.399,358.409z M90.356,322.083
v36.325H67.607v-100.2c7.069-1.211,17.024-2.114,31.032-2.114c14.164,0,24.266,2.709,31.042,8.139
c6.478,5.121,10.833,13.566,10.833,23.497c0,9.954-3.308,18.394-9.326,24.115c-7.833,7.382-19.439,10.684-33,10.684
C95.172,322.535,92.458,322.398,90.356,322.083z M451.534,520.962H96.757v-103.57h354.776V520.962z M449.314,359.91
c-11.598,0-23.04-3.013-28.773-6.173l4.661-18.991c6.18,3.165,15.664,6.336,25.46,6.336c10.552,0,16.121-4.373,16.121-10.992
c0-6.337-4.814-9.959-17.014-14.322c-16.876-5.884-27.884-15.212-27.884-29.983c0-17.336,14.467-30.581,38.416-30.581
c11.463,0,19.89,2.415,25.915,5.124l-5.119,18.528c-4.072-1.959-11.296-4.816-21.248-4.816c-9.943,0-14.763,4.527-14.763,9.797
c0,6.479,5.729,9.328,18.835,14.312c17.942,6.619,26.363,15.969,26.363,30.288C490.295,345.443,477.192,359.91,449.314,359.91z"/>
<path d="M117.765,288.341c0-9.341-6.478-14.915-17.911-14.915c-4.682,0-7.848,0.451-9.503,0.903v29.985
c1.95,0.451,4.37,0.592,7.677,0.592C110.246,304.907,117.765,298.74,117.765,288.341z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -188,6 +188,10 @@ html, body {
display: none !important;
}
.pointer{
.not-visible {
visibility: hidden;
}
.pointer {
cursor: pointer;
}