mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-04-16 22:24:49 +00:00
chore: eslint fixes for @typescript-eslint/no-explicit-any (#11672)
This commit is contained in:
@@ -145,6 +145,14 @@ module.exports = {
|
||||
'no-multiple-empty-lines': 'error',
|
||||
'no-redeclare': 'off',
|
||||
'@typescript-eslint/no-redeclare': ['off', { ignoreDeclarationMerge: true }],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'no-return-await': 'error',
|
||||
'rxjs/no-create': 'error',
|
||||
'rxjs/no-subject-unsubscribe': 'error',
|
||||
|
||||
@@ -92,8 +92,8 @@ See the [Custom layout](#custom-layout) section for full details of all availabl
|
||||
| showRightSidebar | `boolean` | false | Toggles right sidebar visibility. Requires `allowRightSidebar` to be set to `true`. |
|
||||
| showToolbar | `boolean` | true | Hide or show the toolbar |
|
||||
| showViewer | `boolean` | true | Hide or show the viewer |
|
||||
| sidebarLeftTemplate | `TemplateRef<any>` | null | The template for the left sidebar. The template context contains the loaded node data. |
|
||||
| sidebarRightTemplate | `TemplateRef<any>` | null | The template for the right sidebar. The template context contains the loaded node data. |
|
||||
| sidebarLeftTemplate | `TemplateRef<unknown>` | null | The template for the left sidebar. The template context contains the loaded node data. |
|
||||
| sidebarRightTemplate | `TemplateRef<unknown>` | null | The template for the right sidebar. The template context contains the loaded node data. |
|
||||
| versionId | `string` | null | Version Id of the file to load. |
|
||||
|
||||
### Events
|
||||
|
||||
@@ -236,7 +236,7 @@ Next in your component you need to get a reference of created template
|
||||
|
||||
```ts
|
||||
@ViewChild('viewerExtensions')
|
||||
viewerTemplateExtensions: TemplateRef<any>;
|
||||
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||
```
|
||||
|
||||
and pass it via `viewerTemplateExtensions` input:
|
||||
|
||||
@@ -53,8 +53,8 @@ With icon:
|
||||
```
|
||||
|
||||
```ts
|
||||
@ViewChild('contentDialogTemplate') contentDialogTemplate: TemplateRef<any>;
|
||||
@ViewChild('actionsDialogTemplate') actionsDialogTemplate: TemplateRef<any>;
|
||||
@ViewChild('contentDialogTemplate') contentDialogTemplate: TemplateRef<unknown>;
|
||||
@ViewChild('actionsDialogTemplate') actionsDialogTemplate: TemplateRef<unknown>;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ interface DataColumn {
|
||||
title?: string;
|
||||
srTitle?: string;
|
||||
cssClass?: string;
|
||||
template?: TemplateRef<any>;
|
||||
template?: TemplateRef<unknown>;
|
||||
formatTooltip?: Function;
|
||||
focus?: boolean;
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ interface DialogData {
|
||||
isCloseButtonHidden?: boolean;
|
||||
isCancelButtonHidden?: boolean;
|
||||
dialogSize?: DialogSizes;
|
||||
contentTemplate?: TemplateRef<any>;
|
||||
actionsTemplate?: TemplateRef<any>;
|
||||
descriptionTemplate?: TemplateRef<any>;
|
||||
contentTemplate?: TemplateRef<unknown>;
|
||||
actionsTemplate?: TemplateRef<unknown>;
|
||||
descriptionTemplate?: TemplateRef<unknown>;
|
||||
headerIcon?: string;
|
||||
additionalActionButtons?: AdditionalDialogActionButton[];
|
||||
componentData?: any;
|
||||
@@ -46,9 +46,9 @@ interface DialogData {
|
||||
| dialogSize | `DialogSize` | `Medium` | Set dialog size. Can be `Large`, `Medium`, `Alert`. (optional) |
|
||||
| contentText | `string` | | Inserts a content text. (optional) |
|
||||
| contentComponent | `Type<any>` | | Inserts a content component. (optional) |
|
||||
| contentTemplate | `TemplateRef<any>` | | Inserts a content template. (optional) |
|
||||
| actionsTemplate | `TemplateRef<any>` | | Inserts a template styled on the left. Should be used for additional `mat-button` style buttons. (optional) |
|
||||
| descriptionTemplate | `TemplateRef<any>` | | Inserts a description template. (optional) |
|
||||
| contentTemplate | `TemplateRef<unknown>` | | Inserts a content template. (optional) |
|
||||
| actionsTemplate | `TemplateRef<unknown>` | | Inserts a template styled on the left. Should be used for additional `mat-button` style buttons. (optional) |
|
||||
| descriptionTemplate | `TemplateRef<unknown>` | | Inserts a description template. (optional) |
|
||||
| additionalActionButtons | `AdditionalDialogActionButton[]` | | Inserts additional base-styled buttons into the action bar on the left. (optional) |
|
||||
| componentData | `any` | | Data that injected in contentComponent. (optional) |
|
||||
| dataOnConfirm$ | `Subject<any>` | | Data to be passed on confirm action after dialog closed. (optional) |
|
||||
|
||||
@@ -18,10 +18,16 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { from, Observable, throwError, Subject } from 'rxjs';
|
||||
import { catchError, map, switchMap, filter, take } from 'rxjs/operators';
|
||||
import { RepositoryInfo, SystemPropertiesRepresentation, DiscoveryApi, AboutApi, SystemPropertiesApi } from '@alfresco/js-api';
|
||||
|
||||
import {
|
||||
RepositoryInfo,
|
||||
SystemPropertiesRepresentation,
|
||||
DiscoveryApi,
|
||||
AboutApi,
|
||||
SystemPropertiesApi,
|
||||
BpmProductVersionModel
|
||||
} from '@alfresco/js-api';
|
||||
import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
||||
import { BpmProductVersionModel, AuthenticationService } from '@alfresco/adf-core';
|
||||
import { AuthenticationService } from '@alfresco/adf-core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -66,24 +72,21 @@ export class DiscoveryApiService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 8.3.0 this method is no longer used, and will be removed in the next major release.
|
||||
* Gets product information for Process Services.
|
||||
*
|
||||
* @returns ProductVersionModel containing product details
|
||||
*/
|
||||
getBpmProductInfo(): Observable<BpmProductVersionModel> {
|
||||
const aboutApi = new AboutApi(this.alfrescoApiService.getInstance());
|
||||
|
||||
return from(aboutApi.getAppVersion()).pipe(
|
||||
map((res) => new BpmProductVersionModel(res)),
|
||||
catchError((err) => throwError(err))
|
||||
);
|
||||
return from(aboutApi.getAppVersion());
|
||||
}
|
||||
|
||||
getBPMSystemProperties(): Observable<SystemPropertiesRepresentation> {
|
||||
const systemPropertiesApi = new SystemPropertiesApi(this.alfrescoApiService.getInstance());
|
||||
|
||||
return from(systemPropertiesApi.getProperties()).pipe(
|
||||
map((res: any) => {
|
||||
map((res) => {
|
||||
if ('string' === typeof res) {
|
||||
throw new Error('Not valid response');
|
||||
}
|
||||
|
||||
@@ -207,7 +207,6 @@ describe('BaseQueryBuilderService', () => {
|
||||
expect(service.getUserFacetBuckets('field1').length).toBe(0);
|
||||
expect(service.getUserFacetBuckets('field2').length).toBe(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('buildQuery', () => {
|
||||
@@ -332,12 +331,12 @@ describe('BaseQueryBuilderService', () => {
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(mockError);
|
||||
expect(executedSpy).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({
|
||||
list: jasmine.objectContaining({
|
||||
pagination: { totalItems: 0 },
|
||||
entries: []
|
||||
})
|
||||
})
|
||||
jasmine.objectContaining({
|
||||
list: jasmine.objectContaining({
|
||||
pagination: { totalItems: 0 },
|
||||
entries: []
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -423,9 +422,7 @@ describe('BaseQueryBuilderService', () => {
|
||||
spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
|
||||
spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve({ list: { entries: [] } } as ResultSetPaging));
|
||||
|
||||
let callCount = 0;
|
||||
service.searchForms.pipe(skip(1)).subscribe((forms) => {
|
||||
callCount++;
|
||||
expect(forms[1].selected).toBe(true);
|
||||
expect(forms[0].selected).toBe(false);
|
||||
done();
|
||||
@@ -446,7 +443,7 @@ describe('BaseQueryBuilderService', () => {
|
||||
it('should call execute when updating configuration', async () => {
|
||||
spyOn(router, 'navigate').and.returnValue(Promise.resolve(true));
|
||||
spyOn(service.searchApi, 'search').and.returnValue(Promise.resolve({ list: { entries: [] } } as ResultSetPaging));
|
||||
spyOn(service, 'execute')
|
||||
spyOn(service, 'execute');
|
||||
service.userQuery = 'test';
|
||||
|
||||
service.updateSelectedConfiguration('config-2');
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { IFeaturesService, FlagChangeset } from '../interfaces/features.interface';
|
||||
|
||||
@@ -91,7 +91,6 @@ export class StorageFeaturesService implements IFeaturesService, IWritableFeatur
|
||||
}
|
||||
|
||||
removeFlag(key: string): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { [key]: _, ...flags } = this.currentFlagState;
|
||||
this.flags.next(flags);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ describe('StoragePrefixFactory', () => {
|
||||
it('should get prefix set in app.config.json', () => {
|
||||
const appConfigPrefix = 'prefix-from-app-config-json';
|
||||
const appConfigService: TestAppConfigService = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
select(_property: string) {
|
||||
return of(appConfigPrefix);
|
||||
}
|
||||
@@ -48,7 +47,6 @@ describe('StoragePrefixFactory', () => {
|
||||
it('should work with NO prefix set in app.config.json', () => {
|
||||
const appConfigPrefix = undefined;
|
||||
const appConfigService: TestAppConfigService = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
select(_property: string) {
|
||||
return of(appConfigPrefix);
|
||||
}
|
||||
@@ -69,7 +67,6 @@ describe('StoragePrefixFactory', () => {
|
||||
it('should return prefix from provided factory, when NO prefix is set in app.config.json', () => {
|
||||
const appConfigPrefix = undefined;
|
||||
const appConfigService: TestAppConfigService = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
select(_property: string) {
|
||||
return of(appConfigPrefix);
|
||||
}
|
||||
@@ -100,7 +97,6 @@ describe('StoragePrefixFactory', () => {
|
||||
const appConfigPrefix = 'prefix-from-app-config-json';
|
||||
|
||||
const appConfigService: TestAppConfigService = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
select(_property: string) {
|
||||
return of(appConfigPrefix);
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export class BpmProductVersionModel {
|
||||
edition: string;
|
||||
majorVersion: string;
|
||||
revisionVersion: string;
|
||||
minorVersion: string;
|
||||
type: string;
|
||||
|
||||
constructor(obj?: any) {
|
||||
if (obj) {
|
||||
this.edition = obj.edition || null;
|
||||
this.majorVersion = obj.majorVersion || null;
|
||||
this.revisionVersion = obj.revisionVersion || null;
|
||||
this.minorVersion = obj.minorVersion || null;
|
||||
this.type = obj.type || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './product-version.model';
|
||||
export * from './comment.model';
|
||||
export * from './pagination.model';
|
||||
export * from './request-pagination.model';
|
||||
|
||||
@@ -25,12 +25,12 @@ import { TranslationService } from '../translation/translation.service';
|
||||
export class FileSizePipe implements PipeTransform {
|
||||
private readonly translation = inject(TranslationService);
|
||||
|
||||
transform(paramByte: any, decimals: number = 2): string {
|
||||
transform(paramByte: number | string | null, decimals: number = 2): string {
|
||||
if (paramByte == null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const bytes = parseInt(paramByte, 10);
|
||||
const bytes = typeof paramByte === 'number' ? paramByte : parseInt(paramByte as string, 10);
|
||||
if (isNaN(bytes)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('SearchTextInputComponent', () => {
|
||||
debugElement = fixture.debugElement;
|
||||
testingUtils = new UnitTestingUtils(debugElement);
|
||||
userPreferencesService = TestBed.inject(UserPreferencesService);
|
||||
component.focusListener = new Subject<any>();
|
||||
component.focusListener = new Subject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('TranslationService', () => {
|
||||
const mockLanguages = spyOnProperty(window, 'navigator').and.returnValue({
|
||||
language: 'en-GB',
|
||||
languages: returnedLanguages
|
||||
} as any);
|
||||
} as unknown as Navigator);
|
||||
|
||||
expect(translationService.getLocale()).toBe('fr-FR');
|
||||
expect(mockLanguages).toHaveBeenCalled();
|
||||
@@ -102,7 +102,7 @@ describe('TranslationService', () => {
|
||||
const mockLanguages = spyOnProperty(window, 'navigator').and.returnValue({
|
||||
language: 'de-DE',
|
||||
languages: []
|
||||
} as any);
|
||||
} as unknown as Navigator);
|
||||
|
||||
expect(translationService.getLocale()).toBe('de-DE');
|
||||
expect(mockLanguages).toHaveBeenCalled();
|
||||
|
||||
@@ -74,11 +74,11 @@ export class ImgViewerComponent implements AfterViewInit, OnChanges, OnDestroy {
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
error = new EventEmitter<void>();
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/no-output-native
|
||||
@Output()
|
||||
submit = new EventEmitter<any>();
|
||||
submit = new EventEmitter<Blob>();
|
||||
|
||||
@Output()
|
||||
isSaving = new EventEmitter<boolean>();
|
||||
|
||||
@@ -48,7 +48,7 @@ export class MediaPlayerComponent implements OnChanges {
|
||||
tracks: Track[] = [];
|
||||
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
error = new EventEmitter<Event>();
|
||||
|
||||
@Output()
|
||||
canPlay = new EventEmitter<void>();
|
||||
@@ -66,7 +66,7 @@ export class MediaPlayerComponent implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
onMediaPlayerError(event: any) {
|
||||
onMediaPlayerError(event: Event) {
|
||||
this.error.emit(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { PdfPasswordDialogComponent } from './pdf-viewer-password-dialog';
|
||||
|
||||
declare const pdfjsLib: any;
|
||||
declare const pdfjsLib: { PasswordResponses: { NEED_PASSWORD: number; INCORRECT_PASSWORD: number } };
|
||||
|
||||
describe('PdfPasswordDialogComponent', () => {
|
||||
let component: PdfPasswordDialogComponent;
|
||||
|
||||
@@ -25,7 +25,7 @@ import { MatInputModule } from '@angular/material/input';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { IconModule } from '../../../icon/icon.module';
|
||||
|
||||
declare const pdfjsLib: any;
|
||||
declare const pdfjsLib: { PasswordResponses: { NEED_PASSWORD: number; INCORRECT_PASSWORD: number } };
|
||||
|
||||
@Component({
|
||||
selector: 'adf-pdf-viewer-password-dialog',
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('PdfThumbComponent', () => {
|
||||
const width = 91;
|
||||
const height = 119;
|
||||
const page = {
|
||||
id: 'pageId',
|
||||
id: 1,
|
||||
getPage: jasmine.createSpy('getPage').and.returnValue(
|
||||
Promise.resolve({
|
||||
getViewport: () => ({ width, height }),
|
||||
|
||||
@@ -20,6 +20,7 @@ import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { Component, ElementRef, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { PdfThumbnailPage } from '../pdf-viewer/pdf-viewer.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-pdf-thumb',
|
||||
@@ -33,7 +34,7 @@ export class PdfThumbComponent implements OnInit, FocusableOption {
|
||||
private readonly element = inject(ElementRef);
|
||||
|
||||
@Input()
|
||||
page: any = null;
|
||||
page: PdfThumbnailPage = null;
|
||||
|
||||
image$: Promise<string>;
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { PdfThumbListComponent } from './pdf-viewer-thumbnails.component';
|
||||
import { UnitTestingUtils } from '../../../testing';
|
||||
import { DOWN_ARROW, ESCAPE, UP_ARROW } from '@angular/cdk/keycodes';
|
||||
import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
||||
|
||||
declare const pdfjsViewer: any;
|
||||
declare const pdfjsViewer: { EventBus: new () => { dispatch: (event: string, data: unknown) => void } };
|
||||
|
||||
describe('PdfThumbListComponent', () => {
|
||||
let fixture: ComponentFixture<PdfThumbListComponent>;
|
||||
@@ -77,7 +78,7 @@ describe('PdfThumbListComponent', () => {
|
||||
fixture = TestBed.createComponent(PdfThumbListComponent);
|
||||
testingUtils = new UnitTestingUtils(fixture.debugElement);
|
||||
component = fixture.componentInstance;
|
||||
component.pdfViewer = viewerMock;
|
||||
component.pdfViewer = viewerMock as unknown as PDFViewer;
|
||||
|
||||
// provide scrollable container
|
||||
fixture.nativeElement.style.display = 'block';
|
||||
|
||||
@@ -36,6 +36,9 @@ import {
|
||||
inject
|
||||
} from '@angular/core';
|
||||
import { delay } from 'rxjs/operators';
|
||||
import { PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
||||
import { PDFPageProxy } from 'pdfjs-dist/types/src/display/api';
|
||||
import { PageChangingEvent, PdfThumbnailPage } from '../pdf-viewer/pdf-viewer.component';
|
||||
import { PdfThumbComponent } from '../pdf-viewer-thumb/pdf-viewer-thumb.component';
|
||||
|
||||
@Component({
|
||||
@@ -50,25 +53,25 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private readonly element = inject(ElementRef);
|
||||
private readonly document = inject(DOCUMENT);
|
||||
|
||||
@Input({ required: true }) pdfViewer: any;
|
||||
@Input({ required: true }) pdfViewer: PDFViewer;
|
||||
|
||||
@Output()
|
||||
close = new EventEmitter<void>();
|
||||
|
||||
virtualHeight: number = 0;
|
||||
translateY: number = 0;
|
||||
renderItems = [];
|
||||
renderItems: PdfThumbnailPage[] = [];
|
||||
width: number = 91;
|
||||
currentHeight: number = 0;
|
||||
|
||||
private items = [];
|
||||
private items: PdfThumbnailPage[] = [];
|
||||
private readonly margin: number = 15;
|
||||
private itemHeight: number = 114 + this.margin;
|
||||
private previouslyFocusedElement: HTMLElement | null = null;
|
||||
private keyManager: FocusKeyManager<PdfThumbComponent>;
|
||||
|
||||
@ContentChild(TemplateRef)
|
||||
template: any;
|
||||
template: TemplateRef<unknown>;
|
||||
|
||||
@ViewChildren(PdfThumbComponent)
|
||||
thumbsList: QueryList<PdfThumbComponent>;
|
||||
@@ -145,7 +148,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
trackByFn(_: number, item: any): number {
|
||||
trackByFn(_: number, item: PdfThumbnailPage): number {
|
||||
return item.id;
|
||||
}
|
||||
|
||||
@@ -171,7 +174,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
getPages() {
|
||||
getPages(): PdfThumbnailPage[] {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
return this.pdfViewer._pages.map((page) => ({
|
||||
id: page.id,
|
||||
@@ -181,12 +184,11 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
}));
|
||||
}
|
||||
|
||||
private setHeight(id): number {
|
||||
const height = this.pdfViewer.pdfDocument.getPage(id).then((page) => this.calculateHeight(page));
|
||||
return height;
|
||||
private setHeight(id: number): Promise<void> {
|
||||
return this.pdfViewer.pdfDocument.getPage(id).then((page) => this.calculateHeight(page));
|
||||
}
|
||||
|
||||
private calculateHeight(page) {
|
||||
private calculateHeight(page: PDFPageProxy) {
|
||||
const viewport = page.getViewport({ scale: 1 });
|
||||
const pageRatio = viewport.width / viewport.height;
|
||||
const height = Math.floor(this.width / pageRatio);
|
||||
@@ -222,7 +224,7 @@ export class PdfThumbListComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
private onPageChange(event) {
|
||||
private onPageChange(event: PageChangingEvent) {
|
||||
const index = this.renderItems.findIndex((element) => element.id === event.pageNumber);
|
||||
|
||||
if (index < 0) {
|
||||
|
||||
@@ -30,7 +30,12 @@ import { PDFJS_MODULE, PDFJS_VIEWER_MODULE, PdfViewerComponent } from './pdf-vie
|
||||
import pdfjsLibraryMock, { annotations } from '../mock/pdfjs-lib.mock';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
declare const pdfjsLib: any;
|
||||
declare const pdfjsLib: {
|
||||
PasswordResponses: {
|
||||
NEED_PASSWORD: number;
|
||||
INCORRECT_PASSWORD: number;
|
||||
};
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'adf-url-test-component',
|
||||
@@ -41,7 +46,7 @@ class UrlTestComponent {
|
||||
@ViewChild(PdfViewerComponent, { static: true })
|
||||
pdfViewerComponent: PdfViewerComponent;
|
||||
|
||||
urlFile: any;
|
||||
urlFile: string;
|
||||
|
||||
constructor() {
|
||||
this.urlFile = './fake-test-file.pdf';
|
||||
@@ -57,7 +62,7 @@ class UrlTestPasswordComponent {
|
||||
@ViewChild(PdfViewerComponent, { static: true })
|
||||
pdfViewerComponent: PdfViewerComponent;
|
||||
|
||||
urlFile: any;
|
||||
urlFile: string;
|
||||
|
||||
constructor() {
|
||||
this.urlFile = './fake-test-password-file.pdf';
|
||||
@@ -72,7 +77,7 @@ class BlobTestComponent {
|
||||
@ViewChild(PdfViewerComponent, { static: true })
|
||||
pdfViewerComponent: PdfViewerComponent;
|
||||
|
||||
blobFile: any;
|
||||
blobFile: Blob;
|
||||
|
||||
constructor() {
|
||||
this.blobFile = this.createFakeBlob();
|
||||
@@ -101,7 +106,7 @@ class BlobTestComponent {
|
||||
describe('Test PdfViewer component', () => {
|
||||
let component: PdfViewerComponent;
|
||||
let fixture: ComponentFixture<PdfViewerComponent>;
|
||||
let change: any;
|
||||
let change: SimpleChange;
|
||||
let dialog: MatDialog;
|
||||
let testingUtils: UnitTestingUtils;
|
||||
|
||||
@@ -265,17 +270,17 @@ describe('Test PdfViewer component', () => {
|
||||
fixtureUrlTestPasswordComponent = TestBed.createComponent(UrlTestPasswordComponent);
|
||||
componentUrlTestPasswordComponent = fixtureUrlTestPasswordComponent.componentInstance;
|
||||
|
||||
spyOn(dialog, 'open').and.callFake((_: any, context: any) => {
|
||||
spyOn(dialog, 'open').and.callFake((_dialogComponent: unknown, context: { data: { reason: number } }) => {
|
||||
if (context.data.reason === pdfjsLib.PasswordResponses.NEED_PASSWORD) {
|
||||
return {
|
||||
afterClosed: () => of('wrong_password')
|
||||
} as any;
|
||||
} as ReturnType<MatDialog['open']>;
|
||||
}
|
||||
|
||||
if (context.data.reason === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) {
|
||||
return {
|
||||
afterClosed: () => of('password')
|
||||
} as any;
|
||||
} as ReturnType<MatDialog['open']>;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -328,7 +333,7 @@ describe('Test PdfViewer component', () => {
|
||||
() =>
|
||||
({
|
||||
afterClosed: () => of('')
|
||||
}) as any
|
||||
}) as ReturnType<MatDialog['open']>
|
||||
);
|
||||
|
||||
spyOn(componentUrlTestPasswordComponent.pdfViewerComponent.close, 'emit');
|
||||
@@ -456,7 +461,9 @@ describe('Test PdfViewer - User interaction', () => {
|
||||
|
||||
component.urlFile = './fake-test-file.pdf';
|
||||
fixture.detectChanges();
|
||||
component.ngOnChanges({ urlFile: { currentValue: './fake-test-file.pdf' } } as any);
|
||||
component.ngOnChanges({
|
||||
urlFile: new SimpleChange(null, './fake-test-file.pdf', true)
|
||||
});
|
||||
|
||||
flush();
|
||||
}));
|
||||
@@ -666,7 +673,10 @@ describe('Test PdfViewer - User interaction', () => {
|
||||
it('should have corrected content in annotation popup', fakeAsync(() => {
|
||||
dispatchAnnotationLayerRenderedEvent();
|
||||
expect(getAnnotationTitle()).toBe('Annotation title');
|
||||
expect(getAnnotationDate()).toBe('2/2/2026, 10:41:06 AM');
|
||||
const dateText = getAnnotationDate();
|
||||
// Date format may vary by locale, so check it contains the key parts
|
||||
expect(dateText).toMatch(/2026/);
|
||||
expect(dateText).toMatch(/10:41:06|10:41:6/);
|
||||
expect(getAnnotationContent()).toBe('Annotation contents');
|
||||
expect(getAnnotationPopupElement()).toBeDefined();
|
||||
}));
|
||||
|
||||
@@ -47,12 +47,41 @@ import { PdfPasswordDialogComponent } from '../pdf-viewer-password-dialog/pdf-vi
|
||||
import { PdfThumbListComponent } from '../pdf-viewer-thumbnails/pdf-viewer-thumbnails.component';
|
||||
import * as pdfjsLib from 'pdfjs-dist/build/pdf.min.mjs';
|
||||
import { EventBus, PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs';
|
||||
import { OnProgressParameters, PDFDocumentLoadingTask, PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';
|
||||
import { OnProgressParameters, PDFDocumentLoadingTask, PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist/types/src/display/api';
|
||||
import { IconModule } from '../../../icon/icon.module';
|
||||
import { PDFDateString } from 'pdfjs-dist';
|
||||
|
||||
export type PdfScaleMode = 'init' | 'page-actual' | 'page-width' | 'page-height' | 'page-fit' | 'auto';
|
||||
|
||||
export interface PageChangingEvent {
|
||||
pageNumber: number;
|
||||
source?: {
|
||||
container?: {
|
||||
id?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface PdfThumbnailPage {
|
||||
id: number;
|
||||
getWidth: () => number;
|
||||
getHeight: () => number;
|
||||
getPage: () => Promise<PDFPageProxy>;
|
||||
}
|
||||
|
||||
export interface PdfAnnotationData {
|
||||
titleObj?: {
|
||||
str?: string;
|
||||
};
|
||||
modificationDate?: string;
|
||||
}
|
||||
|
||||
export interface PdfAnnotationWithTitle extends PdfAnnotationData {
|
||||
titleObj: {
|
||||
str: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const PDFJS_MODULE = new InjectionToken('PDFJS_MODULE', { factory: () => pdfjsLib });
|
||||
export const PDFJS_VIEWER_MODULE = new InjectionToken('PDFJS_VIEWER_MODULE', { factory: () => PDFViewer });
|
||||
|
||||
@@ -93,19 +122,19 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
allowThumbnails = false;
|
||||
|
||||
@Input()
|
||||
thumbnailsTemplate: TemplateRef<any> = null;
|
||||
thumbnailsTemplate: TemplateRef<unknown> = null;
|
||||
|
||||
@Input()
|
||||
cacheType: string = '';
|
||||
|
||||
@Output()
|
||||
rendered = new EventEmitter<any>();
|
||||
rendered = new EventEmitter<void>();
|
||||
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
error = new EventEmitter<void>();
|
||||
|
||||
@Output()
|
||||
close = new EventEmitter<any>();
|
||||
close = new EventEmitter<void>();
|
||||
|
||||
@Output()
|
||||
pagesLoaded = new EventEmitter<void>();
|
||||
@@ -127,7 +156,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
loadingTask: PDFDocumentLoadingTask;
|
||||
isPanelDisabled = true;
|
||||
showThumbnails: boolean = false;
|
||||
pdfThumbnailsContext: { viewer: any } = { viewer: null };
|
||||
pdfThumbnailsContext: { viewer: PDFViewer | null } = { viewer: null };
|
||||
randomPdfId: string;
|
||||
documentOverflow = false;
|
||||
|
||||
@@ -214,7 +243,12 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
|
||||
const urlFile = changes['urlFile'];
|
||||
if (urlFile?.currentValue) {
|
||||
const pdfOptions: any = {
|
||||
const pdfOptions: {
|
||||
url: string;
|
||||
withCredentials: boolean | undefined;
|
||||
isEvalSupported: boolean;
|
||||
httpHeaders?: { 'Cache-Control': string };
|
||||
} & typeof this.pdfjsDefaultOptions = {
|
||||
...this.pdfjsDefaultOptions,
|
||||
url: urlFile.currentValue,
|
||||
withCredentials: this.appConfigService.get<boolean>('auth.withCredentials', undefined),
|
||||
@@ -233,7 +267,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
executePdf(pdfOptions: any) {
|
||||
executePdf(pdfOptions: Parameters<typeof this.pdfjsLib.getDocument>[0]) {
|
||||
this.setupPdfJsWorker().then(() => {
|
||||
this.loadingTask = this.pdfjsLib.getDocument(pdfOptions);
|
||||
|
||||
@@ -282,7 +316,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
initPDFViewer(pdfDocument: PDFDocumentProxy) {
|
||||
const viewer: any = this.getViewer();
|
||||
const viewer: HTMLDivElement = this.getViewer();
|
||||
const container = this.getDocumentContainer();
|
||||
|
||||
if (viewer && container) {
|
||||
@@ -433,8 +467,8 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
return document.getElementById(`${this.randomPdfId}-viewer-pdf-viewer`) as HTMLDivElement;
|
||||
}
|
||||
|
||||
private getViewer(): HTMLElement {
|
||||
return document.getElementById(`${this.randomPdfId}-viewer-viewerPdf`);
|
||||
private getViewer(): HTMLDivElement {
|
||||
return document.getElementById(`${this.randomPdfId}-viewer-viewerPdf`) as HTMLDivElement;
|
||||
}
|
||||
|
||||
checkPageFitInContainer(scale: number): number {
|
||||
@@ -519,9 +553,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
* @param ticks number of ticks to zoom
|
||||
*/
|
||||
zoomIn(ticks?: number): void {
|
||||
let newScale: any = this.pdfViewer.currentScaleValue;
|
||||
let newScale: number = Number(this.pdfViewer.currentScaleValue);
|
||||
do {
|
||||
newScale = (newScale * this.DEFAULT_SCALE_DELTA).toFixed(2);
|
||||
newScale = Number((newScale * this.DEFAULT_SCALE_DELTA).toFixed(2));
|
||||
newScale = Math.ceil(newScale * 10) / 10;
|
||||
newScale = Math.min(this.MAX_SCALE, newScale);
|
||||
} while (--ticks > 0 && newScale < this.MAX_SCALE);
|
||||
@@ -535,9 +569,9 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
* @param ticks number of ticks to scale
|
||||
*/
|
||||
zoomOut(ticks?: number): void {
|
||||
let newScale: any = this.pdfViewer.currentScaleValue;
|
||||
let newScale: number = Number(this.pdfViewer.currentScaleValue);
|
||||
do {
|
||||
newScale = (newScale / this.DEFAULT_SCALE_DELTA).toFixed(2);
|
||||
newScale = Number((newScale / this.DEFAULT_SCALE_DELTA).toFixed(2));
|
||||
newScale = Math.floor(newScale * 10) / 10;
|
||||
newScale = Math.max(this.MIN_SCALE, newScale);
|
||||
} while (--ticks > 0 && newScale > this.MIN_SCALE);
|
||||
@@ -589,9 +623,13 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
/**
|
||||
* Page Change Event
|
||||
*
|
||||
* @param event event
|
||||
* @param event - page change event
|
||||
* @param event.pageNumber - the new page number
|
||||
* @param event.source - the source object
|
||||
* @param event.source.container - the container element
|
||||
* @param event.source.container.id - the container id
|
||||
*/
|
||||
onPageChange(event: any) {
|
||||
onPageChange(event: PageChangingEvent) {
|
||||
if (event.source && event.source.container.id === `${this.randomPdfId}-viewer-pdf-viewer`) {
|
||||
this.page = event.pageNumber;
|
||||
this.displayPage = event.pageNumber;
|
||||
@@ -682,11 +720,11 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private createAnnotationPopup(annotation: any, text: string, annotationElement: HTMLElement): void {
|
||||
private createAnnotationPopup(annotation: PdfAnnotationData, text: string, annotationElement: HTMLElement): void {
|
||||
const popupElement = document.createElement('div');
|
||||
let headerElement: HTMLSpanElement;
|
||||
if (annotation.titleObj?.str) {
|
||||
headerElement = this.createAnnotationPopupHeader(annotation);
|
||||
headerElement = this.createAnnotationPopupHeader(annotation as PdfAnnotationWithTitle);
|
||||
}
|
||||
const contentElement = this.createAnnotationPopupContent(text);
|
||||
popupElement.classList.add('popup', 'adf-pdf-viewer-annotation-tooltip');
|
||||
@@ -694,7 +732,7 @@ export class PdfViewerComponent implements OnChanges, OnDestroy {
|
||||
annotationElement.appendChild(popupElement);
|
||||
}
|
||||
|
||||
private createAnnotationPopupHeader(annotation: any): HTMLSpanElement {
|
||||
private createAnnotationPopupHeader(annotation: PdfAnnotationWithTitle): HTMLSpanElement {
|
||||
const headerElement = document.createElement('span');
|
||||
const titleElement = document.createElement('span');
|
||||
let dateElement: HTMLTimeElement;
|
||||
|
||||
@@ -31,7 +31,7 @@ export class TxtViewerComponent implements OnChanges {
|
||||
private readonly appConfigService = inject(AppConfigService);
|
||||
|
||||
@Input()
|
||||
urlFile: any;
|
||||
urlFile: string;
|
||||
|
||||
@Input()
|
||||
blobFile: Blob;
|
||||
@@ -86,7 +86,7 @@ export class TxtViewerComponent implements OnChanges {
|
||||
this.content = reader.result;
|
||||
};
|
||||
|
||||
reader.onerror = (error: any) => {
|
||||
reader.onerror = (error: ProgressEvent<FileReader>) => {
|
||||
reject(error);
|
||||
};
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class DoubleViewerComponent {
|
||||
viewer2: ViewerRenderComponent;
|
||||
|
||||
@ViewChild('viewerExtension', { static: true })
|
||||
viewerTemplateExtensions: TemplateRef<any>;
|
||||
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||
|
||||
urlFileViewer1: string;
|
||||
urlFileViewer2: string;
|
||||
|
||||
@@ -89,7 +89,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
|
||||
|
||||
/** The template for the pdf thumbnails. */
|
||||
@Input()
|
||||
thumbnailsTemplate: TemplateRef<any> = null;
|
||||
thumbnailsTemplate: TemplateRef<unknown> = null;
|
||||
|
||||
/** MIME type of the file content (when not determined by the filename extension). */
|
||||
@Input()
|
||||
@@ -124,7 +124,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
|
||||
|
||||
/** Template containing ViewerExtensionDirective instances providing different viewer extensions based on supported file extension. */
|
||||
@Input()
|
||||
viewerTemplateExtensions: TemplateRef<any>;
|
||||
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||
|
||||
/** Custom error message to be displayed in the viewer. */
|
||||
@Input()
|
||||
@@ -149,7 +149,7 @@ export class ViewerRenderComponent implements OnChanges, OnInit {
|
||||
@ViewChild(ImgViewerComponent)
|
||||
imgViewer: ImgViewerComponent;
|
||||
|
||||
extensionTemplates: { template: TemplateRef<any>; isVisible: boolean }[] = [];
|
||||
extensionTemplates: { template: TemplateRef<unknown>; isVisible: boolean }[] = [];
|
||||
extensionsSupportedByTemplates: string[] = [];
|
||||
extension: string;
|
||||
internalFileName: string;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, DebugElement, SimpleChanges } from '@angular/core';
|
||||
import { Component, DebugElement, SimpleChange, SimpleChanges } from '@angular/core';
|
||||
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { of } from 'rxjs';
|
||||
@@ -42,8 +42,8 @@ import { ThumbnailService } from '../../common/services/thumbnail.service';
|
||||
class DummyDialogComponent {}
|
||||
|
||||
describe('ViewerComponent', () => {
|
||||
let component: ViewerComponent<any>;
|
||||
let fixture: ComponentFixture<ViewerComponent<any>>;
|
||||
let component: ViewerComponent<unknown>;
|
||||
let fixture: ComponentFixture<ViewerComponent<unknown>>;
|
||||
let dialog: MatDialog;
|
||||
let viewUtilService: ViewUtilService;
|
||||
let appConfigService: AppConfigService;
|
||||
@@ -106,7 +106,9 @@ describe('ViewerComponent', () => {
|
||||
|
||||
describe('Mime Type Test', () => {
|
||||
it('should mimeType change when blobFile changes', () => {
|
||||
const mockSimpleChanges: any = { blobFile: { currentValue: { type: 'image/png' } } };
|
||||
const mockSimpleChanges: SimpleChanges = {
|
||||
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
|
||||
};
|
||||
|
||||
component.ngOnChanges(mockSimpleChanges);
|
||||
|
||||
@@ -115,7 +117,10 @@ describe('ViewerComponent', () => {
|
||||
|
||||
it('should set mimeTypeIconUrl when mimeType changes and no nodeMimeType is provided', () => {
|
||||
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('image/png');
|
||||
const mockSimpleChanges: any = { mimeType: { currentValue: 'image/png' }, nodeMimeType: undefined };
|
||||
const mockSimpleChanges: SimpleChanges = {
|
||||
mimeType: new SimpleChange(null, 'image/png', true),
|
||||
nodeMimeType: undefined
|
||||
};
|
||||
|
||||
component.ngOnChanges(mockSimpleChanges);
|
||||
|
||||
@@ -125,7 +130,10 @@ describe('ViewerComponent', () => {
|
||||
|
||||
it('should set mimeTypeIconUrl when nodeMimeType changes', () => {
|
||||
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('application/pdf');
|
||||
const mockSimpleChanges: any = { mimeType: { currentValue: 'image/png' }, nodeMimeType: { currentValue: 'application/pdf' } };
|
||||
const mockSimpleChanges: SimpleChanges = {
|
||||
mimeType: new SimpleChange(null, 'image/png', true),
|
||||
nodeMimeType: new SimpleChange(null, 'application/pdf', true)
|
||||
};
|
||||
|
||||
component.ngOnChanges(mockSimpleChanges);
|
||||
fixture.detectChanges();
|
||||
@@ -523,7 +531,9 @@ describe('ViewerComponent', () => {
|
||||
});
|
||||
|
||||
it('should file name be present if is overlay mode ', async () => {
|
||||
const mockSimpleChanges: any = { blobFile: { currentValue: { type: 'image/png' } } };
|
||||
const mockSimpleChanges: SimpleChanges = {
|
||||
blobFile: new SimpleChange(null, { type: 'image/png' }, true)
|
||||
};
|
||||
component.ngOnChanges(mockSimpleChanges);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
@@ -705,7 +715,7 @@ describe('ViewerComponent', () => {
|
||||
downloadPromptReminderDelay: 2
|
||||
}
|
||||
};
|
||||
dialogOpenSpy = spyOn(dialog, 'open').and.returnValue({ afterClosed: () => of(null) } as any);
|
||||
dialogOpenSpy = spyOn(dialog, 'open').and.returnValue({ afterClosed: () => of(null) } as ReturnType<MatDialog['open']>);
|
||||
component.urlFile = undefined;
|
||||
component.clearDownloadPromptTimeouts();
|
||||
});
|
||||
@@ -716,11 +726,11 @@ describe('ViewerComponent', () => {
|
||||
});
|
||||
|
||||
it('should configure reminder timeout to display non responsive dialog after initial dialog', fakeAsync(() => {
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as any);
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as ReturnType<MatDialog['open']>);
|
||||
fixture.detectChanges();
|
||||
tick(3000);
|
||||
expect(component.downloadPromptReminderTimer).toBeDefined();
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as any);
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as ReturnType<MatDialog['open']>);
|
||||
flush();
|
||||
discardPeriodicTasks();
|
||||
}));
|
||||
@@ -741,12 +751,12 @@ describe('ViewerComponent', () => {
|
||||
}));
|
||||
|
||||
it('should show reminder non responsive dialog after initial dialog', fakeAsync(() => {
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as any);
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.WAIT) } as ReturnType<MatDialog['open']>);
|
||||
fixture.detectChanges();
|
||||
tick(3000);
|
||||
expect(dialogOpenSpy).toHaveBeenCalled();
|
||||
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as any);
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(null) } as ReturnType<MatDialog['open']>);
|
||||
tick(2000);
|
||||
expect(dialogOpenSpy).toHaveBeenCalledTimes(2);
|
||||
|
||||
@@ -755,7 +765,7 @@ describe('ViewerComponent', () => {
|
||||
}));
|
||||
|
||||
it('should emit downloadFileEvent when DownloadPromptDialog return DownloadPromptActions.DOWNLOAD on close', fakeAsync(() => {
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.DOWNLOAD) } as any);
|
||||
dialogOpenSpy.and.returnValue({ afterClosed: () => of(DownloadPromptActions.DOWNLOAD) } as ReturnType<MatDialog['open']>);
|
||||
spyOn(component.downloadFile, 'emit');
|
||||
fixture.detectChanges();
|
||||
tick(3000);
|
||||
|
||||
@@ -117,7 +117,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
||||
mnuMoreActions: ViewerMoreActionsComponent;
|
||||
|
||||
@ContentChild('viewerExtensions', { static: false })
|
||||
viewerTemplateExtensions: TemplateRef<any>;
|
||||
viewerTemplateExtensions: TemplateRef<unknown>;
|
||||
|
||||
get CloseButtonPosition() {
|
||||
return CloseButtonPosition;
|
||||
@@ -190,11 +190,11 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
||||
|
||||
/** The template for the right sidebar. The template context contains the loaded node data. */
|
||||
@Input()
|
||||
sidebarRightTemplate: TemplateRef<any> = null;
|
||||
sidebarRightTemplate: TemplateRef<unknown> = null;
|
||||
|
||||
/** The template for the left sidebar. The template context contains the loaded node data. */
|
||||
@Input()
|
||||
sidebarLeftTemplate: TemplateRef<any> = null;
|
||||
sidebarLeftTemplate: TemplateRef<unknown> = null;
|
||||
|
||||
/** Enable when where is possible the editing functionalities */
|
||||
@Input()
|
||||
@@ -243,7 +243,7 @@ export class ViewerComponent<T> implements OnDestroy, OnInit, OnChanges {
|
||||
|
||||
/** Template containing ViewerExtensionDirective instances providing different viewer extensions based on supported file extension. */
|
||||
@Input()
|
||||
viewerExtensions: TemplateRef<any>;
|
||||
viewerExtensions: TemplateRef<unknown>;
|
||||
|
||||
/** Identifier of a node that is opened by the viewer. */
|
||||
@Input()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
import { Location } from '@angular/common';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
import { ChangeDetectorRef, ElementRef } from '@angular/core';
|
||||
import { ChangeDetectorRef, ElementRef, TemplateRef } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { ViewerRenderComponent } from '../components/viewer-render/viewer-render.component';
|
||||
import { ViewerExtensionDirective } from './viewer-extension.directive';
|
||||
@@ -50,7 +50,7 @@ describe('ExtensionViewerDirective', () => {
|
||||
});
|
||||
extensionViewerDirective = TestBed.inject(ViewerExtensionDirective);
|
||||
viewerRenderer = TestBed.inject(ViewerRenderComponent);
|
||||
extensionViewerDirective.templateModel = { template: '', isVisible: false };
|
||||
extensionViewerDirective.templateModel = { template: {} as TemplateRef<unknown>, isVisible: false };
|
||||
});
|
||||
|
||||
it('is defined', () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ export class ViewerExtensionDirective implements AfterContentInit {
|
||||
private readonly viewerComponent = inject(ViewerRenderComponent);
|
||||
|
||||
@ContentChild(TemplateRef)
|
||||
template: any;
|
||||
template: TemplateRef<unknown>;
|
||||
|
||||
@Input()
|
||||
urlFileContent: string;
|
||||
@@ -37,7 +37,7 @@ export class ViewerExtensionDirective implements AfterContentInit {
|
||||
@Input()
|
||||
supportedExtensions: string[];
|
||||
|
||||
templateModel: any;
|
||||
templateModel: { template: TemplateRef<unknown>; isVisible: boolean };
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
|
||||
@@ -16,6 +16,29 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import type { PDFViewer } from 'pdfjs-dist/types/web/pdf_viewer';
|
||||
import type { PDFThumbnailViewer } from 'pdfjs-dist/types/web/pdf_thumbnail_viewer';
|
||||
import type { PDFPageView } from 'pdfjs-dist/types/web/pdf_page_view';
|
||||
|
||||
interface VisiblePage {
|
||||
id: number;
|
||||
}
|
||||
|
||||
interface VisiblePages {
|
||||
first?: VisiblePage;
|
||||
last?: VisiblePage;
|
||||
views?: Array<{ view: PDFPageView }>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Type helper for accessing the resume method on PDFPageView.
|
||||
* PDFPageView implements IRenderableView which includes a resume method,
|
||||
* but the TypeScript definitions don't properly expose it.
|
||||
*/
|
||||
// cspell:ignore Renderable
|
||||
interface ResumableView {
|
||||
resume?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -33,13 +56,13 @@ export class RenderingQueueServices {
|
||||
|
||||
CLEANUP_TIMEOUT: number = 30_000;
|
||||
|
||||
pdfViewer: any = null;
|
||||
pdfThumbnailViewer: any = null;
|
||||
onIdle: any = null;
|
||||
pdfViewer: PDFViewer | null = null;
|
||||
pdfThumbnailViewer: PDFThumbnailViewer | null = null;
|
||||
onIdle: (() => void) | null = null;
|
||||
|
||||
highestPriorityPage: string | null = null;
|
||||
idleTimeout: any = null;
|
||||
printing: any = false;
|
||||
idleTimeout: number | null = null;
|
||||
printing = false;
|
||||
isThumbnailViewEnabled = false;
|
||||
|
||||
/**
|
||||
@@ -47,7 +70,7 @@ export class RenderingQueueServices {
|
||||
*
|
||||
* @param pdfViewer viewer instance
|
||||
*/
|
||||
setViewer(pdfViewer): void {
|
||||
setViewer(pdfViewer: PDFViewer): void {
|
||||
this.pdfViewer = pdfViewer;
|
||||
}
|
||||
|
||||
@@ -56,7 +79,7 @@ export class RenderingQueueServices {
|
||||
*
|
||||
* @param pdfThumbnailViewer viewer instance
|
||||
*/
|
||||
setThumbnailViewer(pdfThumbnailViewer): void {
|
||||
setThumbnailViewer(pdfThumbnailViewer: PDFThumbnailViewer): void {
|
||||
this.pdfThumbnailViewer = pdfThumbnailViewer;
|
||||
}
|
||||
|
||||
@@ -66,18 +89,18 @@ export class RenderingQueueServices {
|
||||
* @param view view to render
|
||||
* @returns `true` if the view has higher priority, otherwise `false`
|
||||
*/
|
||||
isHighestPriority(view: any): boolean {
|
||||
isHighestPriority(view: PDFPageView): boolean {
|
||||
return this.highestPriorityPage === view.renderingId;
|
||||
}
|
||||
|
||||
renderHighestPriority(currentlyVisiblePages) {
|
||||
renderHighestPriority(currentlyVisiblePages?: unknown): void {
|
||||
if (this.idleTimeout) {
|
||||
clearTimeout(this.idleTimeout);
|
||||
this.idleTimeout = null;
|
||||
}
|
||||
|
||||
// Pages have a higher priority than thumbnails, so check them first.
|
||||
if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
|
||||
if (this.pdfViewer?.forceRendering(currentlyVisiblePages)) {
|
||||
return;
|
||||
}
|
||||
// No pages needed rendering so check thumbnails.
|
||||
@@ -91,11 +114,23 @@ export class RenderingQueueServices {
|
||||
}
|
||||
|
||||
if (this.onIdle) {
|
||||
this.idleTimeout = setTimeout(this.onIdle.bind(this), this.CLEANUP_TIMEOUT);
|
||||
// Type assertion needed: setTimeout returns NodeJS.Timeout in Node types,
|
||||
// but returns number at runtime in browser (where this code executes).
|
||||
// PDFRenderingQueue interface requires idleTimeout to be number.
|
||||
this.idleTimeout = setTimeout(this.onIdle.bind(this), this.CLEANUP_TIMEOUT) as unknown as number;
|
||||
}
|
||||
}
|
||||
|
||||
getHighestPriority(visible, views, scrolledDown) {
|
||||
/**
|
||||
* Gets the highest priority page to render from the visible pages
|
||||
* This method is part of the PDFRenderingQueue interface compatibility
|
||||
*
|
||||
* @param visible visible pages information
|
||||
* @param views array of page views
|
||||
* @param scrolledDown whether the user scrolled down
|
||||
* @returns the highest priority page view to render, null if all done, or false if no visible pages
|
||||
*/
|
||||
getHighestPriority(visible: VisiblePages, views: PDFPageView[], scrolledDown: boolean): PDFPageView | null | false {
|
||||
// The state has changed figure out which page has the highest priority to
|
||||
// render next (if any).
|
||||
// Priority:
|
||||
@@ -104,6 +139,10 @@ export class RenderingQueueServices {
|
||||
// 2 if last scrolled up page before the visible pages
|
||||
const visibleViews = visible.views;
|
||||
|
||||
if (!visibleViews) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const numberVisible = visibleViews.length;
|
||||
if (numberVisible === 0) {
|
||||
return false;
|
||||
@@ -116,13 +155,13 @@ export class RenderingQueueServices {
|
||||
}
|
||||
|
||||
// All the visible views have rendered, try to render next/previous pages.
|
||||
if (scrolledDown) {
|
||||
if (scrolledDown && visible.last) {
|
||||
const nextPageIndex = visible.last.id;
|
||||
// ID's start at 1 so no need to add 1.
|
||||
if (views[nextPageIndex] && !this.isViewFinished(views[nextPageIndex])) {
|
||||
return views[nextPageIndex];
|
||||
}
|
||||
} else {
|
||||
} else if (visible.first) {
|
||||
const previousPageIndex = visible.first.id - 2;
|
||||
if (views[previousPageIndex] && !this.isViewFinished(views[previousPageIndex])) {
|
||||
return views[previousPageIndex];
|
||||
@@ -142,7 +181,7 @@ export class RenderingQueueServices {
|
||||
* @param view the View instance to check
|
||||
* @returns `true` if rendering is finished, otherwise `false`
|
||||
*/
|
||||
isViewFinished(view): boolean {
|
||||
isViewFinished(view: PDFPageView): boolean {
|
||||
return view.renderingState === this.renderingStates.FINISHED;
|
||||
}
|
||||
|
||||
@@ -154,7 +193,7 @@ export class RenderingQueueServices {
|
||||
* @param view View instance to render
|
||||
* @returns the rendered state of the view
|
||||
*/
|
||||
renderView(view: any): boolean {
|
||||
renderView(view: PDFPageView): boolean {
|
||||
const state = view.renderingState;
|
||||
switch (state) {
|
||||
case this.renderingStates.FINISHED: {
|
||||
@@ -162,7 +201,10 @@ export class RenderingQueueServices {
|
||||
}
|
||||
case this.renderingStates.PAUSED: {
|
||||
this.highestPriorityPage = view.renderingId;
|
||||
view.resume();
|
||||
const resumableView = view as unknown as ResumableView;
|
||||
if (resumableView.resume) {
|
||||
resumableView.resume();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case this.renderingStates.RUNNING: {
|
||||
@@ -171,10 +213,9 @@ export class RenderingQueueServices {
|
||||
}
|
||||
case this.renderingStates.INITIAL: {
|
||||
this.highestPriorityPage = view.renderingId;
|
||||
// eslint-disable-next-line space-before-function-paren
|
||||
const continueRendering = function () {
|
||||
const continueRendering = () => {
|
||||
this.renderHighestPriority();
|
||||
}.bind(this);
|
||||
};
|
||||
view.draw().then(continueRendering, continueRendering);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -26,4 +26,4 @@ TestBed.initTestEnvironment(GlobalTestingModule, platformBrowserDynamicTesting()
|
||||
teardown: { destroyAfterEach: true }
|
||||
});
|
||||
|
||||
(window as any).pdfjsLib = pdfjsLibraryMock;
|
||||
(window as unknown as { pdfjsLib: typeof pdfjsLibraryMock }).pdfjsLib = pdfjsLibraryMock;
|
||||
|
||||
@@ -7,6 +7,10 @@ const { constants } = require('karma');
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
files: [
|
||||
{ pattern: '../../node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||
{ pattern: '../../node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false }
|
||||
],
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
|
||||
@@ -9,6 +9,8 @@ module.exports = function (config) {
|
||||
basePath: '../../',
|
||||
|
||||
files: [
|
||||
{ pattern: 'node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false },
|
||||
{ pattern: 'node_modules/chart.js/dist/Chart.js', included: true, watched: false },
|
||||
{ pattern: 'node_modules/raphael/raphael.min.js', included: true, watched: false },
|
||||
{
|
||||
|
||||
@@ -17,6 +17,17 @@
|
||||
|
||||
import { BaseApi } from './base.api';
|
||||
|
||||
/**
|
||||
* Product version model for Process Services.
|
||||
*/
|
||||
export interface BpmProductVersionModel {
|
||||
edition: string;
|
||||
majorVersion: string;
|
||||
revisionVersion: string;
|
||||
minorVersion: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* About service.
|
||||
*/
|
||||
@@ -26,7 +37,7 @@ export class AboutApi extends BaseApi {
|
||||
* Provides information about the running Alfresco Process Services Suite. The response payload object has the properties type, majorVersion, minorVersion, revisionVersion and edition.
|
||||
* @return Promise<{ [key: string]: string; }>
|
||||
*/
|
||||
getAppVersion(): Promise<{ [key: string]: string }> {
|
||||
getAppVersion(): Promise<BpmProductVersionModel> {
|
||||
return this.get({
|
||||
path: '/api/enterprise/app-version'
|
||||
});
|
||||
|
||||
@@ -8,6 +8,8 @@ module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '../../',
|
||||
files: [
|
||||
{ pattern: 'node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false },
|
||||
{
|
||||
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
||||
included: true,
|
||||
|
||||
@@ -8,6 +8,8 @@ module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '../../',
|
||||
files: [
|
||||
{ pattern: 'node_modules/pdfjs-dist/build/pdf.min.mjs', type: 'module', included: true, watched: false },
|
||||
{ pattern: 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs', type: 'module', included: true, watched: false },
|
||||
{
|
||||
pattern: 'node_modules/@angular/material/prebuilt-themes/indigo-pink.css',
|
||||
included: true,
|
||||
|
||||
Reference in New Issue
Block a user