RED-3988: extract some events

This commit is contained in:
Dan Percic 2022-05-20 19:18:15 +03:00
parent 428975cd4e
commit 5c3c2b222c
7 changed files with 158 additions and 122 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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