RED-3988: extract some events
This commit is contained in:
parent
428975cd4e
commit
5c3c2b222c
@ -24,8 +24,7 @@ import { environment } from '@environments/environment';
|
||||
import { AnnotationDrawService } from '../../services/annotation-draw.service';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { BASE_HREF } from '../../../../tokens';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens';
|
||||
import { AutoUnsubscribe, ConfirmationDialogInput, ErrorService, LoadingService, log } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { toPosition } from '../../utils/pdf-calculation.utils';
|
||||
@ -33,7 +32,7 @@ import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { PageRotationService } from '../../services/page-rotation.service';
|
||||
import { ALLOWED_KEYBOARD_SHORTCUTS, HeaderElements, TextPopups } from '../../utils/constants';
|
||||
import { HeaderElements, TextPopups } from '../../utils/constants';
|
||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
||||
import { loadCompareDocumentWrapper } from '../../utils/compare-mode.utils';
|
||||
import { from } from 'rxjs';
|
||||
@ -55,16 +54,20 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
@Input() canPerformActions = false;
|
||||
@Output() readonly annotationSelected = this.#annotationSelected$;
|
||||
@Output() readonly manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
||||
@Output() readonly pageChanged = new EventEmitter<number>();
|
||||
@Output() readonly keyUp = new EventEmitter<KeyboardEvent>();
|
||||
@Output() readonly pageChanged = this.pdf.pageChanged$.pipe(tap(() => this._handleCustomActions()));
|
||||
@Output() readonly keyUp = this.pdf.keyUp$;
|
||||
@ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
|
||||
instance: WebViewerInstance;
|
||||
documentViewer: Core.DocumentViewer;
|
||||
annotationManager: Core.AnnotationManager;
|
||||
private _selectedText = '';
|
||||
readonly #visibilityOffIcon = this._convertPath('/assets/icons/general/visibility-off.svg');
|
||||
readonly #visibilityIcon = this._convertPath('/assets/icons/general/visibility.svg');
|
||||
readonly #searchIcon = this._convertPath('/assets/icons/general/pdftron-action-search.svg');
|
||||
readonly #falsePositiveIcon = this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg');
|
||||
readonly #addRedactionIcon = this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg');
|
||||
readonly #addDictIcon = this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg');
|
||||
|
||||
constructor(
|
||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualRedactionService: ManualRedactionService,
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
@ -72,7 +75,6 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _pageRotationService: PageRotationService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
@ -80,8 +82,8 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
readonly pdf: PdfViewer,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
private readonly _state: FilePreviewStateService,
|
||||
private readonly _multiSelectService: MultiSelectService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -93,11 +95,11 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
ngOnInit() {
|
||||
this._loadViewer();
|
||||
|
||||
this.addActiveScreenSubscription = this.stateService.blob$
|
||||
this.addActiveScreenSubscription = this._state.blob$
|
||||
.pipe(
|
||||
log('Reload blob'),
|
||||
switchMap(blob => from(this.pdf.lockDocument()).pipe(map(() => blob))),
|
||||
withLatestFrom(this.stateService.file$),
|
||||
withLatestFrom(this._state.file$),
|
||||
tap(() => this._errorService.clear()),
|
||||
tap(([blob, file]) => this.pdf.loadDocument(blob, file)),
|
||||
tap(() => this._pageRotationService.clearRotationsHideActions()),
|
||||
@ -124,16 +126,16 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
fileReader.onload = async () => {
|
||||
const pdfNet = this.instance.Core.PDFNet;
|
||||
|
||||
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||
await pdfNet.initialize(environment.licenseKey ? window.atob(environment.licenseKey) : null);
|
||||
|
||||
const compareDocument = await pdfNet.PDFDoc.createFromBuffer(fileReader.result as ArrayBuffer);
|
||||
const blob = await this.stateService.blob;
|
||||
const blob = await this._state.blob;
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||
|
||||
const loadCompareDocument = async () => {
|
||||
this._loadingService.start();
|
||||
const mergedDocument = await pdfNet.PDFDoc.create();
|
||||
const file = this.stateService.file;
|
||||
const file = this._state.file;
|
||||
await loadCompareDocumentWrapper(
|
||||
currentDocument,
|
||||
compareDocument,
|
||||
@ -184,13 +186,13 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
|
||||
if (action === 'deselected') {
|
||||
// Remove deselected annotations from selected list
|
||||
nextAnnotations = this.annotationManager.getSelectedAnnotations().filter(ann => !annotations.some(a => a.Id === ann.Id));
|
||||
} else if (!this.multiSelectService.isEnabled) {
|
||||
nextAnnotations = this._annotationManager.selectedAnnotations.filter(ann => !annotations.some(a => a.Id === ann.Id));
|
||||
} else if (!this._multiSelectService.isEnabled) {
|
||||
// Only choose the last selected annotation, to bypass viewer multi select
|
||||
nextAnnotations = annotations;
|
||||
} else {
|
||||
// Get selected annotations from the manager, no intervention needed
|
||||
nextAnnotations = this.annotationManager.getSelectedAnnotations();
|
||||
nextAnnotations = this._annotationManager.selectedAnnotations;
|
||||
}
|
||||
|
||||
// this.annotationSelected.emit(nextAnnotations.map(ann => ann.Id));
|
||||
@ -199,7 +201,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
return nextAnnotations.map(ann => ann.Id);
|
||||
}
|
||||
|
||||
if (!this.multiSelectService.isEnabled) {
|
||||
if (!this._multiSelectService.isEnabled) {
|
||||
const notSelected = this._fileDataService.all.filter(wrapper => !nextAnnotations.some(ann => ann.Id === wrapper.id));
|
||||
this._annotationManager.deselectAnnotations(notSelected);
|
||||
}
|
||||
@ -211,87 +213,35 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
|
||||
private _loadViewer() {
|
||||
this.instance = this.pdf.instance;
|
||||
this.documentViewer = this.pdf.documentViewer;
|
||||
this.annotationManager = this.pdf.annotationManager;
|
||||
|
||||
this._configureElements();
|
||||
this._configureTextPopup();
|
||||
|
||||
this.annotationManager.addEventListener('annotationChanged', (annotations: Annotation[]) => {
|
||||
// when a rectangle is drawn,
|
||||
// it returns one annotation with tool name 'AnnotationCreateRectangle;
|
||||
// this will auto select rectangle after drawing
|
||||
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
|
||||
this.annotationManager.selectAnnotations(annotations);
|
||||
annotations[0].disableRotationControl();
|
||||
}
|
||||
});
|
||||
|
||||
this.documentViewer.addEventListener('pageNumberUpdated', (pageNumber: number) => {
|
||||
this._annotationManager.deselectAnnotations();
|
||||
console.log(pageNumber);
|
||||
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
|
||||
return this._handleCustomActions();
|
||||
});
|
||||
|
||||
this.documentViewer.addEventListener('keyUp', ($event: KeyboardEvent) => {
|
||||
// arrows and full-screen
|
||||
if (($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input') {
|
||||
if ($event.key.startsWith('Arrow') || $event.key === 'f') {
|
||||
this._ngZone.run(() => this.keyUp.emit($event));
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
if (!ALLOWED_KEYBOARD_SHORTCUTS.includes($event.key)) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
}
|
||||
});
|
||||
|
||||
this.documentViewer.addEventListener('textSelected', (quads, selectedText, pageNumber: number) => {
|
||||
this.pdf.documentViewer.addEventListener('textSelected', (quads, selectedText, pageNumber: number) => {
|
||||
this._selectedText = selectedText;
|
||||
const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
|
||||
|
||||
const file = this.stateService.file;
|
||||
|
||||
if (this.pdf.isCompare && pageNumber % 2 === 0) {
|
||||
this.instance.UI.disableElements(['textPopup']);
|
||||
this.pdf.disable('textPopup');
|
||||
} else {
|
||||
this.instance.UI.enableElements(['textPopup']);
|
||||
this.pdf.enable('textPopup');
|
||||
}
|
||||
|
||||
const isCurrentPageExcluded = file.isPageExcluded(this.pdf.currentPage);
|
||||
const isCurrentPageExcluded = this._state.file.isPageExcluded(this.pdf.currentPage);
|
||||
const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
|
||||
|
||||
if (selectedText.length > 2 && this.canPerformActions && !isCurrentPageExcluded) {
|
||||
this.instance.UI.enableElements(textActions);
|
||||
this.pdf.enable(textActions);
|
||||
} else {
|
||||
this.instance.UI.disableElements(textActions);
|
||||
this.pdf.disable(textActions);
|
||||
}
|
||||
});
|
||||
|
||||
this.instance.UI.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
|
||||
if (event.detail.element === 'searchPanel') {
|
||||
const inputElement = this.instance.UI.iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
|
||||
setTimeout(() => {
|
||||
inputElement.value = '';
|
||||
}, 0);
|
||||
if (!event.detail.isVisible) {
|
||||
this.documentViewer.clearSearchResults();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _convertPath(path: string): string {
|
||||
return this._baseHref + path;
|
||||
}
|
||||
|
||||
private _toggleRectangleAnnotationAction(readonly = false) {
|
||||
if (!readonly) {
|
||||
this.instance.UI.enableElements([TextPopups.ADD_RECTANGLE]);
|
||||
this.pdf.enable(TextPopups.ADD_RECTANGLE);
|
||||
} else {
|
||||
this.instance.UI.disableElements([TextPopups.ADD_RECTANGLE]);
|
||||
this.pdf.disable(TextPopups.ADD_RECTANGLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,13 +249,8 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
this._headerConfigService.initialize(this.compareFileInput);
|
||||
|
||||
const dossierTemplateId = this.dossier.dossierTemplateId;
|
||||
|
||||
this.documentViewer.getTool('AnnotationCreateRectangle').setStyles({
|
||||
StrokeThickness: 2,
|
||||
StrokeColor: this._annotationDrawService.getAndConvertColor(dossierTemplateId, 'manual'),
|
||||
FillColor: this._annotationDrawService.getAndConvertColor(dossierTemplateId, 'manual'),
|
||||
Opacity: 0.6,
|
||||
});
|
||||
const color = this._annotationDrawService.getAndConvertColor(dossierTemplateId, 'manual');
|
||||
this.pdf.setRectangleToolStyles(color);
|
||||
}
|
||||
|
||||
#configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) {
|
||||
@ -332,18 +277,16 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
this.instance.UI.annotationPopup.add([
|
||||
{
|
||||
type: 'actionButton',
|
||||
img: allAreVisible
|
||||
? this._convertPath('/assets/icons/general/visibility-off.svg')
|
||||
: this._convertPath('/assets/icons/general/visibility.svg'),
|
||||
img: allAreVisible ? this.#visibilityOffIcon : this.#visibilityIcon,
|
||||
title: this._translateService.instant(`annotation-actions.${allAreVisible ? 'hide' : 'show'}`),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
if (allAreVisible) {
|
||||
this.annotationManager.hideAnnotations(viewerAnnotations);
|
||||
this._annotationManager.hideAnnotations(viewerAnnotations);
|
||||
} else {
|
||||
this.annotationManager.showAnnotations(viewerAnnotations);
|
||||
this._annotationManager.showAnnotations(viewerAnnotations);
|
||||
}
|
||||
this.annotationManager.deselectAllAnnotations();
|
||||
this._annotationManager.deselectAnnotations();
|
||||
this._fileDataService.updateHiddenAnnotations(viewerAnnotations, allAreVisible);
|
||||
});
|
||||
},
|
||||
@ -361,7 +304,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
{
|
||||
type: 'actionButton',
|
||||
dataElement: TextPopups.ADD_RECTANGLE,
|
||||
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
|
||||
img: this.#addRedactionIcon,
|
||||
title: this.#getTitle(ManualRedactionEntryTypes.REDACTION),
|
||||
onClick: () => this._addRectangleManualRedaction(),
|
||||
},
|
||||
@ -370,7 +313,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
}
|
||||
|
||||
private _addRectangleManualRedaction() {
|
||||
const activeAnnotation = this.annotationManager.getSelectedAnnotations()[0];
|
||||
const activeAnnotation = this._annotationManager.selectedAnnotations[0];
|
||||
const activePage = activeAnnotation.getPageNumber();
|
||||
const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation)];
|
||||
const manualRedactionEntry = this._getManualRedaction({ [activePage]: quads });
|
||||
@ -387,10 +330,10 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
private _configureTextPopup() {
|
||||
const searchButton = {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/pdftron-action-search.svg'),
|
||||
img: this.#searchIcon,
|
||||
title: this._translateService.instant('pdf-viewer.text-popup.actions.search'),
|
||||
onClick: () => {
|
||||
const text = this.documentViewer.getSelectedText();
|
||||
const text = this.pdf.documentViewer.getSelectedText();
|
||||
const searchOptions = {
|
||||
caseSensitive: true, // match case
|
||||
wholeWord: true, // match whole words only
|
||||
@ -410,7 +353,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
popups.push({
|
||||
type: 'actionButton',
|
||||
dataElement: TextPopups.ADD_FALSE_POSITIVE,
|
||||
img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'),
|
||||
img: this.#falsePositiveIcon,
|
||||
title: this.#getTitle(ManualRedactionEntryTypes.FALSE_POSITIVE),
|
||||
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.FALSE_POSITIVE),
|
||||
});
|
||||
@ -419,14 +362,14 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
popups.push({
|
||||
type: 'actionButton',
|
||||
dataElement: TextPopups.ADD_REDACTION,
|
||||
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
|
||||
img: this.#addRedactionIcon,
|
||||
title: this.#getTitle(ManualRedactionEntryTypes.REDACTION),
|
||||
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION),
|
||||
});
|
||||
popups.push({
|
||||
type: 'actionButton',
|
||||
dataElement: TextPopups.ADD_DICTIONARY,
|
||||
img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'),
|
||||
img: this.#addDictIcon,
|
||||
title: this.#getTitle(ManualRedactionEntryTypes.DICTIONARY),
|
||||
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY),
|
||||
});
|
||||
@ -441,8 +384,8 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
}
|
||||
|
||||
private _addManualRedactionOfType(type: ManualRedactionEntryType) {
|
||||
const selectedQuads: Readonly<Record<string, Core.Math.Quad[]>> = this.documentViewer.getSelectedTextQuads();
|
||||
const text = this.documentViewer.getSelectedText();
|
||||
const selectedQuads: Readonly<Record<string, Core.Math.Quad[]>> = this.pdf.documentViewer.getSelectedTextQuads();
|
||||
const text = this.pdf.documentViewer.getSelectedText();
|
||||
const manualRedactionEntry = this._getManualRedaction(selectedQuads, text, true);
|
||||
this.manualAnnotationRequested.emit({ manualRedactionEntry, type });
|
||||
}
|
||||
@ -455,7 +398,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
HeaderElements.ROTATE_RIGHT_BUTTON,
|
||||
];
|
||||
|
||||
const isCurrentPageExcluded = this.stateService.file.isPageExcluded(this.pdf.currentPage);
|
||||
const isCurrentPageExcluded = this._state.file.isPageExcluded(this.pdf.currentPage);
|
||||
|
||||
if (this.canPerformActions && !isCurrentPageExcluded) {
|
||||
try {
|
||||
@ -463,11 +406,11 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
} catch (e) {
|
||||
// happens
|
||||
}
|
||||
this.instance.UI.enableElements(textPopupsToToggle);
|
||||
this.pdf.enable(textPopupsToToggle);
|
||||
this._headerConfigService.enable(headerItemsToToggle);
|
||||
|
||||
if (this._selectedText.length > 2) {
|
||||
this.instance.UI.enableElements([TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]);
|
||||
this.pdf.enable([TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -488,7 +431,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
this.instance.UI.disableTools(['AnnotationCreateRectangle']);
|
||||
}
|
||||
|
||||
this.instance.UI.disableElements(textPopupElementsToDisable);
|
||||
this.pdf.disable(textPopupElementsToDisable);
|
||||
this._headerConfigService.disable(headerElementsToDisable);
|
||||
}
|
||||
|
||||
@ -502,7 +445,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
for (const key of Object.keys(quads)) {
|
||||
for (const quad of quads[key]) {
|
||||
const page = parseInt(key, 10);
|
||||
const pageHeight = this.documentViewer.getPageHeight(page);
|
||||
const pageHeight = this.pdf.documentViewer.getPageHeight(page);
|
||||
entry.positions.push(toPosition(page, pageHeight, convertQuads ? this.#translateQuad(page, quad) : quad));
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,8 +11,6 @@ export const ActionsHelpModeKeys = {
|
||||
'hint-image': 'picture',
|
||||
} as const;
|
||||
|
||||
export const ALLOWED_KEYBOARD_SHORTCUTS: List = ['+', '-', 'p', 'r', 'Escape'] as const;
|
||||
|
||||
export const ALL_HOTKEYS: List = ['Escape', 'F', 'f', 'ArrowUp', 'ArrowDown'] as const;
|
||||
|
||||
export type HeaderElementType =
|
||||
|
||||
@ -1,19 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import type { List } from '@iqser/common-ui';
|
||||
import { DeleteAnnotationsOptions } from '@shared/components/reusable-pdf-viewer/types';
|
||||
import { AnnotationPredicate, DeleteAnnotationsOptions } from '@shared/components/reusable-pdf-viewer/types';
|
||||
import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { fromEvent, Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { getIds } from '@shared/components/reusable-pdf-viewer/functions';
|
||||
import AnnotationManager = Core.AnnotationManager;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
type AnnotationPredicate = (value: Annotation) => boolean;
|
||||
|
||||
function getIds(items?: List<string | AnnotationWrapper>): List | undefined {
|
||||
return items?.map(value => (typeof value === 'string' ? value : value.id));
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
@ -37,6 +32,7 @@ export class REDAnnotationManager {
|
||||
init(annotationManager: AnnotationManager) {
|
||||
this.#manager = annotationManager;
|
||||
this.annotationSelected$ = this.#annotationSelected$;
|
||||
this.#autoSelectRectangleAfterCreation();
|
||||
}
|
||||
|
||||
deleteAnnotation(annotation: AnnotationWrapper | string) {
|
||||
@ -90,6 +86,18 @@ export class REDAnnotationManager {
|
||||
this.#manager.showAnnotations(annotations);
|
||||
}
|
||||
|
||||
#autoSelectRectangleAfterCreation() {
|
||||
this.#manager.addEventListener('annotationChanged', (annotations: Annotation[]) => {
|
||||
// when a rectangle is drawn,
|
||||
// it returns one annotation with tool name 'AnnotationCreateRectangle;
|
||||
// this will auto select rectangle after drawing
|
||||
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
|
||||
this.#manager.selectAnnotations(annotations);
|
||||
annotations[0].disableRotationControl();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#getAnnotationsById(ids: List) {
|
||||
return ids.map(id => this.#manager.getAnnotationById(id)).filter(a => !!a);
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { CustomError } from '@iqser/common-ui';
|
||||
import { CustomError, List } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
export const ALLOWED_KEYBOARD_SHORTCUTS: List = ['+', '-', 'p', 'r', 'Escape'] as const;
|
||||
|
||||
export const DOCUMENT_LOADING_ERROR = new CustomError(_('error.file-preview.label'), _('error.file-preview.action'), 'iqser:refresh');
|
||||
|
||||
export const USELESS_ELEMENTS = [
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { List } from '@iqser/common-ui';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { ALLOWED_KEYBOARD_SHORTCUTS } from '@shared/components/reusable-pdf-viewer/constants';
|
||||
|
||||
export function stopAndPrevent<T extends Event>($event: T) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
}
|
||||
|
||||
export function stopAndPreventIfNotAllowed($event: KeyboardEvent) {
|
||||
if (!ALLOWED_KEYBOARD_SHORTCUTS.includes($event.key)) {
|
||||
stopAndPrevent($event);
|
||||
}
|
||||
}
|
||||
|
||||
export function getIds(items?: List<string | AnnotationWrapper>): List | undefined {
|
||||
return items?.map(value => (typeof value === 'string' ? value : value.id));
|
||||
}
|
||||
|
||||
export function asList(dataElements: string[] | string): string[] {
|
||||
return typeof dataElements === 'string' ? [dataElements] : dataElements;
|
||||
}
|
||||
@ -3,20 +3,23 @@ import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/w
|
||||
import { environment } from '@environments/environment';
|
||||
import { BASE_HREF_FN } from '../../../../tokens';
|
||||
import { File } from '@red/domain';
|
||||
import { ErrorService, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||
import { ErrorService, log, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators';
|
||||
import { debounceTime, distinctUntilChanged, filter, map, startWith, tap } from 'rxjs/operators';
|
||||
import { BehaviorSubject, combineLatest, fromEvent, merge, Observable } from 'rxjs';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, USELESS_ELEMENTS } from './constants';
|
||||
import { Rgb } from '@shared/components/reusable-pdf-viewer/types';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { asList, stopAndPrevent, stopAndPreventIfNotAllowed } from '@shared/components/reusable-pdf-viewer/functions';
|
||||
import { REDAnnotationManager } from '@shared/components/reusable-pdf-viewer/annotation-manager.service';
|
||||
import AnnotationManager = Core.AnnotationManager;
|
||||
import TextTool = Core.Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
|
||||
import DocumentViewer = Core.DocumentViewer;
|
||||
import Color = Core.Annotations.Color;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -34,8 +37,10 @@ export class PdfViewer {
|
||||
|
||||
loaded$: Observable<boolean>;
|
||||
pageComplete$: Observable<unknown>;
|
||||
pageChanged$: Observable<number>;
|
||||
compareMode$: Observable<boolean>;
|
||||
totalPages$: Observable<number>;
|
||||
keyUp$: Observable<KeyboardEvent>;
|
||||
|
||||
#instance: WebViewerInstance;
|
||||
readonly #compareMode$ = new BehaviorSubject(false);
|
||||
@ -43,6 +48,7 @@ export class PdfViewer {
|
||||
constructor(
|
||||
private readonly _logger: NGXLogger,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _injector: Injector,
|
||||
) {}
|
||||
@ -81,8 +87,18 @@ export class PdfViewer {
|
||||
return this.isCompare ? Math.ceil(currentInternalPage / 2) : currentInternalPage;
|
||||
}
|
||||
|
||||
get #keyUp$() {
|
||||
return fromEvent<KeyboardEvent>(this.documentViewer, 'keyUp').pipe(
|
||||
tap(stopAndPreventIfNotAllowed),
|
||||
filter($event => ($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input'),
|
||||
filter($event => $event.key.startsWith('Arrow') || $event.key === 'f'),
|
||||
tap(stopAndPrevent),
|
||||
log('[PDF] Keyboard shortcut'),
|
||||
);
|
||||
}
|
||||
|
||||
get #totalPages$() {
|
||||
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged');
|
||||
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged').pipe(startWith(''));
|
||||
const pageCount$ = layoutChanged$.pipe(
|
||||
map(() => this.pageCount),
|
||||
distinctUntilChanged(),
|
||||
@ -123,6 +139,11 @@ export class PdfViewer {
|
||||
return this.documentViewer.getCurrentPage();
|
||||
}
|
||||
|
||||
get #pageChanged$() {
|
||||
const page$ = fromEvent<number>(this.documentViewer, 'pageNumberUpdated');
|
||||
return page$.pipe(tap(() => this._annotationManager.deselectAnnotations()));
|
||||
}
|
||||
|
||||
navigateTo(page: string | number) {
|
||||
const parsedNumber = typeof page === 'string' ? parseInt(page, 10) : page;
|
||||
const paginationOffset = this.#paginationOffset;
|
||||
@ -152,14 +173,34 @@ export class PdfViewer {
|
||||
this.loaded$ = merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast());
|
||||
this.compareMode$ = this.#compareMode$.asObservable();
|
||||
this.pageComplete$ = this.#pageComplete$.pipe(shareLast());
|
||||
this.pageChanged$ = this.#pageChanged$.pipe(shareLast());
|
||||
this.totalPages$ = this.#totalPages$.pipe(shareDistinctLast());
|
||||
this.keyUp$ = this.#keyUp$;
|
||||
this.#setSelectionMode();
|
||||
this.#configureElements();
|
||||
this.#disableHotkeys();
|
||||
this.#clearSearchResultsWhenVisibilityChanged();
|
||||
|
||||
return this.#instance;
|
||||
}
|
||||
|
||||
setRectangleToolStyles(color: Color) {
|
||||
this.documentViewer.getTool('AnnotationCreateRectangle').setStyles({
|
||||
StrokeThickness: 2,
|
||||
StrokeColor: color,
|
||||
FillColor: color,
|
||||
Opacity: 0.6,
|
||||
});
|
||||
}
|
||||
|
||||
enable(dataElements: string[] | string) {
|
||||
this.#instance.UI.enableElements(asList(dataElements));
|
||||
}
|
||||
|
||||
disable(dataElements: string[] | string) {
|
||||
this.#instance.UI.disableElements(asList(dataElements));
|
||||
}
|
||||
|
||||
getPageHeight(page: number) {
|
||||
try {
|
||||
return this.documentViewer.getPageHeight(page);
|
||||
@ -235,6 +276,23 @@ export class PdfViewer {
|
||||
return annotation instanceof this.#instance.Core.Annotations.TextHighlightAnnotation;
|
||||
}
|
||||
|
||||
#clearSearchResultsWhenVisibilityChanged() {
|
||||
const iframeWindow = this.#instance.UI.iframeWindow;
|
||||
iframeWindow.addEventListener('visibilityChanged', (event: any) => {
|
||||
if (event.detail.element !== 'searchPanel') {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputElement = iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
|
||||
|
||||
setTimeout(() => (inputElement.value = ''), 0);
|
||||
|
||||
if (!event.detail.isVisible) {
|
||||
this.documentViewer.clearSearchResults();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#setInitialDisplayMode() {
|
||||
this.#instance.UI.setFitMode('FitPage');
|
||||
const displayModeManager = this.documentViewer.getDisplayModeManager();
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
export interface Rgb {
|
||||
readonly r: number;
|
||||
readonly g: number;
|
||||
@ -10,3 +13,5 @@ export interface DeleteAnnotationsOptions {
|
||||
readonly isUndoRedo?: boolean;
|
||||
readonly source?: string;
|
||||
}
|
||||
|
||||
export type AnnotationPredicate = (value: Annotation) => boolean;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user