@forgeroute/ui 0.4.57 → 0.4.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -19,6 +19,26 @@ export interface PdfViewerField {
|
|
|
19
19
|
label: string;
|
|
20
20
|
tone?: PdfViewerTone;
|
|
21
21
|
}
|
|
22
|
+
/** State of a clickable spot over a page. */
|
|
23
|
+
export type PdfViewerHotspotState = 'ready' | 'blocked' | 'signed';
|
|
24
|
+
/** A clickable box drawn over a page (for example "Sign here"). Positions are PDF points with the origin at the page's bottom-left corner. */
|
|
25
|
+
export interface PdfViewerHotspot {
|
|
26
|
+
id: string;
|
|
27
|
+
/** 1-based page number; a hotspot on a page that does not exist is ignored. */
|
|
28
|
+
page: number;
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
/** `ready` = a button, `blocked` = the same button disabled (shows `blockedLabel`), `signed` = shows `image` instead of a button. */
|
|
34
|
+
state: PdfViewerHotspotState;
|
|
35
|
+
label?: string;
|
|
36
|
+
blockedLabel?: string;
|
|
37
|
+
ariaLabel?: string;
|
|
38
|
+
/** The image (a data URL) shown while `state` is `signed`. */
|
|
39
|
+
image?: string | null;
|
|
40
|
+
imageAlt?: string;
|
|
41
|
+
}
|
|
22
42
|
export interface PdfViewerLoadedEvent {
|
|
23
43
|
pageCount: number;
|
|
24
44
|
/** Every page's size in PDF points at scale 1. */
|
|
@@ -45,6 +65,15 @@ interface PageView {
|
|
|
45
65
|
canvasWidth: number;
|
|
46
66
|
canvasHeight: number;
|
|
47
67
|
fields: FieldView[];
|
|
68
|
+
hotspots: HotspotView[];
|
|
69
|
+
}
|
|
70
|
+
interface HotspotView {
|
|
71
|
+
hotspot: PdfViewerHotspot;
|
|
72
|
+
left: number;
|
|
73
|
+
top: number;
|
|
74
|
+
width: number;
|
|
75
|
+
height: number;
|
|
76
|
+
fontPx: number;
|
|
48
77
|
}
|
|
49
78
|
interface FieldView {
|
|
50
79
|
id: string;
|
|
@@ -96,6 +125,7 @@ export declare class PdfViewerUnsupportedError extends Error {
|
|
|
96
125
|
* - `maxPageWidth` (default 700): pages are drawn this wide, or the container's width when narrower; never larger.
|
|
97
126
|
* - `pixelRatio`: canvas pixels per CSS pixel; default is the screen's ratio between 1 and 3.
|
|
98
127
|
* - `fields`: badges positioned in PDF points (origin bottom-left); they scale with the page.
|
|
128
|
+
* - `hotspots`: clickable boxes (e.g. "Sign here") in the same coordinates; `ready` is a button, `blocked` a disabled one, `signed` shows an image. `(hotspotActivate)` gives the id.
|
|
99
129
|
* - `loaded` / `pagesRendered` / `loadFailed`: see the event types. A new `src` / `data` replaces the document; a slow older load never paints.
|
|
100
130
|
* A page that fails to draw after `loaded` also emits `loadFailed`, shows the error state and destroys the document that `loaded` handed to the host.
|
|
101
131
|
* - A page's canvas never exceeds 16,000,000 pixels (Safari limit); the pixel ratio is lowered for such a page, its CSS size is unchanged.
|
|
@@ -127,6 +157,8 @@ export declare class PdfViewerComponent {
|
|
|
127
157
|
/** Canvas pixels per CSS pixel; null = the screen's ratio, limited to 1..3. */
|
|
128
158
|
readonly pixelRatio: import("@angular/core").InputSignal<number | null>;
|
|
129
159
|
readonly fields: import("@angular/core").InputSignal<PdfViewerField[]>;
|
|
160
|
+
/** Clickable boxes over the pages (e.g. "Sign here"), in PDF points with the origin bottom-left; `(hotspotActivate)` gives the id of the one clicked. */
|
|
161
|
+
readonly hotspots: import("@angular/core").InputSignal<PdfViewerHotspot[]>;
|
|
130
162
|
/** Accessible name of the viewer. */
|
|
131
163
|
readonly ariaLabel: import("@angular/core").InputSignal<string>;
|
|
132
164
|
readonly loadingText: import("@angular/core").InputSignal<string>;
|
|
@@ -137,6 +169,8 @@ export declare class PdfViewerComponent {
|
|
|
137
169
|
readonly openText: import("@angular/core").InputSignal<string>;
|
|
138
170
|
/** Check for the browser features pdf.js needs before loading it; false = always try to load. */
|
|
139
171
|
readonly checkBrowserSupport: import("@angular/core").InputSignal<boolean>;
|
|
172
|
+
/** The id of the hotspot that was clicked (only a `ready` hotspot can be). */
|
|
173
|
+
readonly hotspotActivate: import("@angular/core").OutputEmitterRef<string>;
|
|
140
174
|
readonly loaded: import("@angular/core").OutputEmitterRef<PdfViewerLoadedEvent>;
|
|
141
175
|
readonly loadFailed: import("@angular/core").OutputEmitterRef<unknown>;
|
|
142
176
|
readonly pagesRendered: import("@angular/core").OutputEmitterRef<PdfViewerRenderedEvent>;
|
|
@@ -226,6 +260,11 @@ export declare class PdfViewerComponent {
|
|
|
226
260
|
"required": false;
|
|
227
261
|
"isSignal": true;
|
|
228
262
|
};
|
|
263
|
+
"hotspots": {
|
|
264
|
+
"alias": "hotspots";
|
|
265
|
+
"required": false;
|
|
266
|
+
"isSignal": true;
|
|
267
|
+
};
|
|
229
268
|
"ariaLabel": {
|
|
230
269
|
"alias": "ariaLabel";
|
|
231
270
|
"required": false;
|
|
@@ -257,6 +296,7 @@ export declare class PdfViewerComponent {
|
|
|
257
296
|
"isSignal": true;
|
|
258
297
|
};
|
|
259
298
|
}, {
|
|
299
|
+
"hotspotActivate": "hotspotActivate";
|
|
260
300
|
"loaded": "loaded";
|
|
261
301
|
"loadFailed": "loadFailed";
|
|
262
302
|
"pagesRendered": "pagesRendered";
|
|
@@ -274,4 +314,229 @@ export interface ForgeroutePdfOptions {
|
|
|
274
314
|
* A `workerSrc` input on a viewer still wins over this value. Throws at provide time when `workerSrc` is blank, because a viewer
|
|
275
315
|
* without a worker can only show "PDF worker is not configured.".
|
|
276
316
|
*/
|
|
277
|
-
export declare function provideForgeroutePdf(options: ForgeroutePdfOptions): EnvironmentProviders;
|
|
317
|
+
export declare function provideForgeroutePdf(options: ForgeroutePdfOptions): EnvironmentProviders;
|
|
318
|
+
/**
|
|
319
|
+
* A dialog that shows one PDF with Download and Print buttons: the file name as the title, the pages below (`ui-pdf-viewer`), a skeleton
|
|
320
|
+
* while the host is still fetching the bytes, an alert when the fetch failed. The HOST fetches (so its own auth / interceptors apply) and
|
|
321
|
+
* passes the bytes:
|
|
322
|
+
*
|
|
323
|
+
* <ui-document-viewer-modal [open]="open()" [fileName]="name()" [data]="bytes()" [loading]="loading()" [error]="error()" (closed)="close()" />
|
|
324
|
+
*
|
|
325
|
+
* Download saves the bytes under `fileName`; Print opens the browser's print dialog for them (a new tab when the browser blocks the
|
|
326
|
+
* hidden print frame). Both are disabled until there are bytes. The pdf.js worker comes from `provideForgeroutePdf` (or `workerSrc`).
|
|
327
|
+
* Test ids: `document-viewer`, `document-viewer-download`, `document-viewer-print`, `document-viewer-loading`, `document-viewer-error`.
|
|
328
|
+
*/
|
|
329
|
+
export declare class DocumentViewerModalComponent {
|
|
330
|
+
readonly open: import("@angular/core").InputSignal<boolean>;
|
|
331
|
+
/** Shown as the dialog title and used as the Download file name. */
|
|
332
|
+
readonly fileName: import("@angular/core").InputSignal<string>;
|
|
333
|
+
/** The PDF bytes (null until fetched). */
|
|
334
|
+
readonly data: import("@angular/core").InputSignal<ArrayBuffer | Uint8Array<ArrayBufferLike> | null>;
|
|
335
|
+
/** The host is still fetching the bytes: a skeleton shows. */
|
|
336
|
+
readonly loading: import("@angular/core").InputSignal<boolean>;
|
|
337
|
+
/** The host's message when the fetch failed (empty = no error). */
|
|
338
|
+
readonly error: import("@angular/core").InputSignal<string>;
|
|
339
|
+
readonly loadingText: import("@angular/core").InputSignal<string>;
|
|
340
|
+
readonly closed: import("@angular/core").OutputEmitterRef<void>;
|
|
341
|
+
protected readonly hasData: import("@angular/core").Signal<boolean>;
|
|
342
|
+
private blob;
|
|
343
|
+
/** Saves the bytes as a PDF named after `fileName`. */
|
|
344
|
+
protected download(): void;
|
|
345
|
+
/** Opens the browser's print dialog for the PDF through a hidden frame (a new tab when the browser blocks it). */
|
|
346
|
+
protected print(): void;
|
|
347
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DocumentViewerModalComponent, never>;
|
|
348
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<DocumentViewerModalComponent, "ui-document-viewer-modal", never, {
|
|
349
|
+
"open": {
|
|
350
|
+
"alias": "open";
|
|
351
|
+
"required": false;
|
|
352
|
+
"isSignal": true;
|
|
353
|
+
};
|
|
354
|
+
"fileName": {
|
|
355
|
+
"alias": "fileName";
|
|
356
|
+
"required": false;
|
|
357
|
+
"isSignal": true;
|
|
358
|
+
};
|
|
359
|
+
"data": {
|
|
360
|
+
"alias": "data";
|
|
361
|
+
"required": false;
|
|
362
|
+
"isSignal": true;
|
|
363
|
+
};
|
|
364
|
+
"loading": {
|
|
365
|
+
"alias": "loading";
|
|
366
|
+
"required": false;
|
|
367
|
+
"isSignal": true;
|
|
368
|
+
};
|
|
369
|
+
"error": {
|
|
370
|
+
"alias": "error";
|
|
371
|
+
"required": false;
|
|
372
|
+
"isSignal": true;
|
|
373
|
+
};
|
|
374
|
+
"loadingText": {
|
|
375
|
+
"alias": "loadingText";
|
|
376
|
+
"required": false;
|
|
377
|
+
"isSignal": true;
|
|
378
|
+
};
|
|
379
|
+
}, {
|
|
380
|
+
"closed": "closed";
|
|
381
|
+
}, never, never, true, never>;
|
|
382
|
+
}
|
|
383
|
+
/** Where a signature goes: PDF points, origin at the page's bottom-left corner, page numbers from 1. */
|
|
384
|
+
export interface DocumentSignField {
|
|
385
|
+
/** Reported back in the result; defaults to the field's position in the list ("1", "2", …). */
|
|
386
|
+
id?: string;
|
|
387
|
+
page: number;
|
|
388
|
+
x: number;
|
|
389
|
+
y: number;
|
|
390
|
+
width: number;
|
|
391
|
+
height: number;
|
|
392
|
+
}
|
|
393
|
+
export interface DocumentSignedField {
|
|
394
|
+
id: string;
|
|
395
|
+
page: number;
|
|
396
|
+
x: number;
|
|
397
|
+
y: number;
|
|
398
|
+
width: number;
|
|
399
|
+
height: number;
|
|
400
|
+
/** The signature image (PNG data URL) that was applied to this field. */
|
|
401
|
+
signatureImage: string;
|
|
402
|
+
}
|
|
403
|
+
export interface DocumentSignResult {
|
|
404
|
+
/** The signature image the signer created (the same image is on every field). */
|
|
405
|
+
signatureDataUrl: string;
|
|
406
|
+
signedAt: string;
|
|
407
|
+
fields: DocumentSignedField[];
|
|
408
|
+
}
|
|
409
|
+
/** Every text of the dialog; pass only the ones you want to change (`[texts]="{ title: 'Sign the contract' }"`). */
|
|
410
|
+
export interface DocumentSignTexts {
|
|
411
|
+
title: string;
|
|
412
|
+
loading: string;
|
|
413
|
+
step1: string;
|
|
414
|
+
namePlaceholder: string;
|
|
415
|
+
previewEmpty: string;
|
|
416
|
+
saveSignature: string;
|
|
417
|
+
change: string;
|
|
418
|
+
step2: string;
|
|
419
|
+
signHere: string;
|
|
420
|
+
addSignatureFirst: string;
|
|
421
|
+
allSigned: string;
|
|
422
|
+
cancel: string;
|
|
423
|
+
complete: string;
|
|
424
|
+
submitting: string;
|
|
425
|
+
noFields: string;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Shows any PDF and collects a signature on it, wherever the host says the signature goes. Generic on purpose: the HOST fetches the
|
|
429
|
+
* bytes (its own auth, pre-filling, prefilled forms) and says where the signature fields are (`fields`, PDF points, bottom-left origin);
|
|
430
|
+
* the dialog does the rest: the signer types their name, it becomes a handwriting-style image, the signer clicks each field to apply it,
|
|
431
|
+
* and `(signed)` reports the image and every field with its position. The host stores the signature however it needs.
|
|
432
|
+
*
|
|
433
|
+
* <ui-document-sign-modal [open]="open()" [data]="bytes()" [fields]="fields()" [loading]="loading()" [error]="error()"
|
|
434
|
+
* [signerName]="user.name" [submitting]="saving()" (signed)="save($event)" (closed)="open.set(false)" />
|
|
435
|
+
*
|
|
436
|
+
* - `savedSignature` (a data URL, e.g. from the user's profile) skips the typing step; it is scaled down to the standard 600x60 image.
|
|
437
|
+
* `(signatureCreated)` gives the new image so the host can keep it for next time.
|
|
438
|
+
* - Complete is disabled until every field is signed (and while there are no fields); the footer counts "N of M signed".
|
|
439
|
+
* - "Change" clears the signature and every applied field so the signer can start again.
|
|
440
|
+
* - `(signed)` does not close the dialog: the host closes it (`[open]="false"`) when its own save succeeded, or shows `error`.
|
|
441
|
+
* - The pdf.js worker comes from `provideForgeroutePdf` (or the viewer's `workerSrc`).
|
|
442
|
+
* Test ids: `sign-modal`, `sign-loading`, `sign-error`, `signature-step`, `typed-name-input`, `typed-signature-preview`, `save-typed-signature`,
|
|
443
|
+
* `redraw-signature`, `apply-step`, `sign-progress`, `sign-cancel`, `sign-complete`, `pdf-hotspot-<field id>`.
|
|
444
|
+
*/
|
|
445
|
+
export declare class DocumentSignModalComponent {
|
|
446
|
+
readonly open: import("@angular/core").InputSignal<boolean>;
|
|
447
|
+
/** The PDF bytes (null until the host has them). */
|
|
448
|
+
readonly data: import("@angular/core").InputSignal<ArrayBuffer | Uint8Array<ArrayBufferLike> | null>;
|
|
449
|
+
/** Where the signature goes. */
|
|
450
|
+
readonly fields: import("@angular/core").InputSignal<DocumentSignField[]>;
|
|
451
|
+
/** The host is still fetching the document: a skeleton shows. */
|
|
452
|
+
readonly loading: import("@angular/core").InputSignal<boolean>;
|
|
453
|
+
/** The host's error text (empty = none): shown instead of the document. */
|
|
454
|
+
readonly error: import("@angular/core").InputSignal<string>;
|
|
455
|
+
/** Prefills the typed-name box. */
|
|
456
|
+
readonly signerName: import("@angular/core").InputSignal<string>;
|
|
457
|
+
/** A signature the signer made before (PNG data URL): used at once, scaled to the standard size. */
|
|
458
|
+
readonly savedSignature: import("@angular/core").InputSignal<string | null>;
|
|
459
|
+
/** The host is saving the result: Complete shows a spinner and cannot be pressed twice. */
|
|
460
|
+
readonly submitting: import("@angular/core").InputSignal<boolean>;
|
|
461
|
+
/** Texts to change (the rest stay in English). */
|
|
462
|
+
readonly texts: import("@angular/core").InputSignal<Partial<DocumentSignTexts>>;
|
|
463
|
+
/** The signer finished: the signature image and every field with its position. The dialog stays open for the host to close. */
|
|
464
|
+
readonly signed: import("@angular/core").OutputEmitterRef<DocumentSignResult>;
|
|
465
|
+
/** The signer created a new signature (typed name saved): the host may keep it. */
|
|
466
|
+
readonly signatureCreated: import("@angular/core").OutputEmitterRef<string>;
|
|
467
|
+
/** The dialog was dismissed (Cancel, Escape, X, backdrop). */
|
|
468
|
+
readonly closed: import("@angular/core").OutputEmitterRef<void>;
|
|
469
|
+
protected readonly t: import("@angular/core").Signal<DocumentSignTexts>;
|
|
470
|
+
protected readonly typedName: import("@angular/core").WritableSignal<string>;
|
|
471
|
+
protected readonly signature: import("@angular/core").WritableSignal<string | null>;
|
|
472
|
+
/** Field id -> the image applied to it. */
|
|
473
|
+
private readonly applied;
|
|
474
|
+
/** The fields with their ids filled in ("1", "2", … when the host gave none). */
|
|
475
|
+
private readonly resolved;
|
|
476
|
+
protected readonly signedCount: import("@angular/core").Signal<number>;
|
|
477
|
+
protected readonly allSigned: import("@angular/core").Signal<boolean>;
|
|
478
|
+
protected readonly hotspots: import("@angular/core").Signal<PdfViewerHotspot[]>;
|
|
479
|
+
constructor();
|
|
480
|
+
protected cancel(): void;
|
|
481
|
+
/** Turns the typed name into a handwriting-style PNG (600x60, left-aligned) and makes it the signature. */
|
|
482
|
+
protected saveTypedSignature(): Promise<void>;
|
|
483
|
+
/** Clears the signature and everything applied, so the signer starts again. */
|
|
484
|
+
protected changeSignature(): void;
|
|
485
|
+
/** Puts the signature on one field (only possible once there is a signature). */
|
|
486
|
+
protected apply(fieldId: string): void;
|
|
487
|
+
protected complete(): void;
|
|
488
|
+
/** Scales a saved signature down to fit 600x60 (never up), left-aligned and centred vertically, so old oversized images weigh the same as new ones. */
|
|
489
|
+
private normalize;
|
|
490
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<DocumentSignModalComponent, never>;
|
|
491
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<DocumentSignModalComponent, "ui-document-sign-modal", never, {
|
|
492
|
+
"open": {
|
|
493
|
+
"alias": "open";
|
|
494
|
+
"required": false;
|
|
495
|
+
"isSignal": true;
|
|
496
|
+
};
|
|
497
|
+
"data": {
|
|
498
|
+
"alias": "data";
|
|
499
|
+
"required": false;
|
|
500
|
+
"isSignal": true;
|
|
501
|
+
};
|
|
502
|
+
"fields": {
|
|
503
|
+
"alias": "fields";
|
|
504
|
+
"required": false;
|
|
505
|
+
"isSignal": true;
|
|
506
|
+
};
|
|
507
|
+
"loading": {
|
|
508
|
+
"alias": "loading";
|
|
509
|
+
"required": false;
|
|
510
|
+
"isSignal": true;
|
|
511
|
+
};
|
|
512
|
+
"error": {
|
|
513
|
+
"alias": "error";
|
|
514
|
+
"required": false;
|
|
515
|
+
"isSignal": true;
|
|
516
|
+
};
|
|
517
|
+
"signerName": {
|
|
518
|
+
"alias": "signerName";
|
|
519
|
+
"required": false;
|
|
520
|
+
"isSignal": true;
|
|
521
|
+
};
|
|
522
|
+
"savedSignature": {
|
|
523
|
+
"alias": "savedSignature";
|
|
524
|
+
"required": false;
|
|
525
|
+
"isSignal": true;
|
|
526
|
+
};
|
|
527
|
+
"submitting": {
|
|
528
|
+
"alias": "submitting";
|
|
529
|
+
"required": false;
|
|
530
|
+
"isSignal": true;
|
|
531
|
+
};
|
|
532
|
+
"texts": {
|
|
533
|
+
"alias": "texts";
|
|
534
|
+
"required": false;
|
|
535
|
+
"isSignal": true;
|
|
536
|
+
};
|
|
537
|
+
}, {
|
|
538
|
+
"signed": "signed";
|
|
539
|
+
"signatureCreated": "signatureCreated";
|
|
540
|
+
"closed": "closed";
|
|
541
|
+
}, never, never, true, never>;
|
|
542
|
+
}
|