@ngstarter-ui/components 21.0.33 → 21.0.35
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/ai/component-registry.json +49 -28
- package/fesm2022/ngstarter-ui-components-form-field.mjs +2 -2
- package/fesm2022/ngstarter-ui-components-form-field.mjs.map +1 -1
- package/fesm2022/ngstarter-ui-components-icon.mjs +118 -7
- package/fesm2022/ngstarter-ui-components-icon.mjs.map +1 -1
- package/fesm2022/ngstarter-ui-components-panel.mjs +1 -1
- package/fesm2022/ngstarter-ui-components-pdf-viewer.mjs +915 -52
- package/fesm2022/ngstarter-ui-components-pdf-viewer.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ngstarter-ui-components-icon.d.ts +27 -6
- package/types/ngstarter-ui-components-panel.d.ts +1 -1
- package/types/ngstarter-ui-components-pdf-viewer.d.ts +205 -5
|
@@ -1,13 +1,153 @@
|
|
|
1
|
-
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
|
1
|
+
import { NgTemplateOutlet, DOCUMENT, isPlatformBrowser } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import {
|
|
3
|
+
import { input, output, signal, computed, ChangeDetectionStrategy, Component, Directive, Injectable, effect, untracked, inject, DestroyRef, ElementRef, PLATFORM_ID, viewChild, contentChildren, numberAttribute, booleanAttribute, afterNextRender } from '@angular/core';
|
|
4
|
+
import { Rotation, MatchFlag } from '@embedpdf/models';
|
|
4
5
|
import { BlockLoader } from '@ngstarter-ui/components/block-loader';
|
|
5
6
|
import { Button } from '@ngstarter-ui/components/button';
|
|
7
|
+
import { Divider } from '@ngstarter-ui/components/divider';
|
|
6
8
|
import { Icon } from '@ngstarter-ui/components/icon';
|
|
7
9
|
import { ImagePlaceholder } from '@ngstarter-ui/components/image-placeholder';
|
|
8
10
|
import { Menu, MenuDivider, MenuHeading, MenuItem, MenuTrigger } from '@ngstarter-ui/components/menu';
|
|
9
|
-
import { Panel, PanelContent, PanelHeader, PanelSidebar } from '@ngstarter-ui/components/panel';
|
|
11
|
+
import { Panel, PanelContent, PanelHeader, PanelSubheader, PanelAside, PanelSidebar } from '@ngstarter-ui/components/panel';
|
|
12
|
+
import { Toolbar, ToolbarItem, ToolbarSpacer, ToolbarTitle } from '@ngstarter-ui/components/toolbar';
|
|
13
|
+
import { isObservable } from 'rxjs';
|
|
14
|
+
import { FormField, IconButtonSuffix, IconPrefix } from '@ngstarter-ui/components/form-field';
|
|
15
|
+
import { Input } from '@ngstarter-ui/components/input';
|
|
10
16
|
import { PluginRegistry } from '@embedpdf/core';
|
|
17
|
+
import { Checkbox } from '@ngstarter-ui/components/checkbox';
|
|
18
|
+
|
|
19
|
+
class PdfViewerAnnotations {
|
|
20
|
+
annotations = input([], ...(ngDevMode ? [{ debugName: "annotations" }] : /* istanbul ignore next */ []));
|
|
21
|
+
annotationDefs = input([], ...(ngDevMode ? [{ debugName: "annotationDefs" }] : /* istanbul ignore next */ []));
|
|
22
|
+
annotationTypeProperty = input('type', ...(ngDevMode ? [{ debugName: "annotationTypeProperty" }] : /* istanbul ignore next */ []));
|
|
23
|
+
closed = output();
|
|
24
|
+
pageSelected = output();
|
|
25
|
+
filterQuery = signal('', ...(ngDevMode ? [{ debugName: "filterQuery" }] : /* istanbul ignore next */ []));
|
|
26
|
+
filteredAnnotations = computed(() => {
|
|
27
|
+
const query = this.filterQuery().trim().toLocaleLowerCase();
|
|
28
|
+
const annotations = this.annotations();
|
|
29
|
+
if (!query) {
|
|
30
|
+
return annotations;
|
|
31
|
+
}
|
|
32
|
+
return annotations.filter((annotation) => this.annotationMatchesFilter(annotation, query));
|
|
33
|
+
}, ...(ngDevMode ? [{ debugName: "filteredAnnotations" }] : /* istanbul ignore next */ []));
|
|
34
|
+
setFilterQuery(event) {
|
|
35
|
+
this.filterQuery.set(event.target.value);
|
|
36
|
+
}
|
|
37
|
+
clearFilterQuery() {
|
|
38
|
+
this.filterQuery.set('');
|
|
39
|
+
}
|
|
40
|
+
getAnnotationTemplate(annotation, index) {
|
|
41
|
+
const annotationDefs = this.annotationDefs();
|
|
42
|
+
const typeProperty = this.annotationTypeProperty();
|
|
43
|
+
const matchingDef = annotationDefs.find((def) => def.matches(annotation, index, typeProperty))
|
|
44
|
+
?? annotationDefs.find((def) => !def.hasWhen());
|
|
45
|
+
return matchingDef?.template ?? null;
|
|
46
|
+
}
|
|
47
|
+
getAnnotationTemplateContext(annotation, index) {
|
|
48
|
+
return {
|
|
49
|
+
$implicit: annotation,
|
|
50
|
+
annotation,
|
|
51
|
+
index,
|
|
52
|
+
goToPage: (pageNumber) => this.pageSelected.emit(pageNumber),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
getAnnotationTypeLabel(annotation) {
|
|
56
|
+
const label = annotation.label ?? annotation.type;
|
|
57
|
+
return typeof label === 'string' && label.trim().length > 0 ? label : 'Comment';
|
|
58
|
+
}
|
|
59
|
+
getAvatarLabel(annotation) {
|
|
60
|
+
const explicitLabel = annotation.avatarLabel;
|
|
61
|
+
if (typeof explicitLabel === 'string' && explicitLabel.trim().length > 0) {
|
|
62
|
+
return explicitLabel.trim();
|
|
63
|
+
}
|
|
64
|
+
return annotation.author
|
|
65
|
+
.split(/\s+/)
|
|
66
|
+
.filter(Boolean)
|
|
67
|
+
.slice(0, 2)
|
|
68
|
+
.map((part) => part[0]?.toUpperCase())
|
|
69
|
+
.join('');
|
|
70
|
+
}
|
|
71
|
+
getAvatarImage(annotation) {
|
|
72
|
+
return typeof annotation.avatarUrl === 'string' ? annotation.avatarUrl : '';
|
|
73
|
+
}
|
|
74
|
+
getReplyLabel(annotation) {
|
|
75
|
+
return typeof annotation.replyLabel === 'string' && annotation.replyLabel.trim().length > 0
|
|
76
|
+
? annotation.replyLabel
|
|
77
|
+
: 'Reply';
|
|
78
|
+
}
|
|
79
|
+
annotationMatchesFilter(annotation, query) {
|
|
80
|
+
return [
|
|
81
|
+
annotation.author,
|
|
82
|
+
annotation.time,
|
|
83
|
+
annotation.text,
|
|
84
|
+
annotation.type,
|
|
85
|
+
annotation.label,
|
|
86
|
+
`page ${annotation.pageNumber}`,
|
|
87
|
+
]
|
|
88
|
+
.filter((value) => typeof value === 'string')
|
|
89
|
+
.some((value) => value.toLocaleLowerCase().includes(query));
|
|
90
|
+
}
|
|
91
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotations, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
92
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PdfViewerAnnotations, isStandalone: true, selector: "ngs-pdf-viewer-annotations", inputs: { annotations: { classPropertyName: "annotations", publicName: "annotations", isSignal: true, isRequired: false, transformFunction: null }, annotationDefs: { classPropertyName: "annotationDefs", publicName: "annotationDefs", isSignal: true, isRequired: false, transformFunction: null }, annotationTypeProperty: { classPropertyName: "annotationTypeProperty", publicName: "annotationTypeProperty", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", pageSelected: "pageSelected" }, host: { classAttribute: "ngs-pdf-viewer-annotations" }, ngImport: i0, template: "<ngs-panel class=\"pdf-viewer-annotations bg-surface\" aria-label=\"PDF annotations\">\n <ngs-panel-header class=\"p-0\">\n <ngs-toolbar class=\"w-full py-0 pr-2 ps-4\" aria-label=\"PDF annotations toolbar\">\n <ngs-toolbar-title>\n Annotations\n </ngs-toolbar-title>\n <ngs-toolbar-spacer />\n <ngs-toolbar-item>\n <button\n class=\"pdf-viewer-annotations__close\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Close annotations panel\"\n (click)=\"closed.emit()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n </ngs-toolbar-item>\n </ngs-toolbar>\n </ngs-panel-header>\n\n <ngs-panel-subheader autoHeight class=\"px-4 pb-4\">\n <ngs-form-field subscriptHiddenIfEmpty>\n <ngs-icon ngsIconPrefix name=\"fluent:search-24-regular\" />\n <input\n ngsInput\n type=\"text\"\n aria-label=\"Filter annotations\"\n placeholder=\"Filter annotations...\"\n [value]=\"filterQuery()\"\n (input)=\"setFilterQuery($event)\" />\n @if (filterQuery()) {\n <button\n ngsIconButton\n ngsIconButtonSuffix\n type=\"button\"\n aria-label=\"Clear annotation filter\"\n (click)=\"clearFilterQuery()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n }\n </ngs-form-field>\n </ngs-panel-subheader>\n\n <ngs-panel-content>\n <div class=\"flex min-h-full flex-col\">\n <div class=\"flex flex-1 flex-col gap-4 pb-4 px-4\">\n <ng-template #defaultAnnotationTemplate let-annotation let-goToPage=\"goToPage\">\n <article class=\"rounded-2xl border border-border bg-surface p-4 shadow-xs\">\n <header class=\"flex items-start gap-3\">\n <span class=\"flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary-container text-xs font-semibold text-on-primary-container\">\n @if (getAvatarImage(annotation); as avatarImage) {\n <img\n class=\"h-full w-full object-cover\"\n [src]=\"avatarImage\"\n [alt]=\"annotation.author\" />\n } @else {\n {{ getAvatarLabel(annotation) }}\n }\n </span>\n\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-sm font-semibold text-on-surface\">{{ annotation.author }}</span>\n @if (annotation.time) {\n <span class=\"block truncate text-xs text-on-surface-variant\">{{ annotation.time }}</span>\n }\n </span>\n\n <span class=\"rounded-full bg-surface-container-high px-2 py-1 text-xs font-medium tracking-normal text-on-surface-variant uppercase\">\n {{ getAnnotationTypeLabel(annotation) }}\n </span>\n </header>\n\n <p class=\"mt-3 mb-0 text-base leading-relaxed text-on-surface\">{{ annotation.text }}</p>\n\n <footer class=\"mt-4 flex items-center justify-between gap-2\">\n <button\n ngsButton=\"tonal\"\n type=\"button\"\n (click)=\"goToPage(annotation.pageNumber)\">\n Page {{ annotation.pageNumber }}\n </button>\n\n <button ngsButton=\"text\" type=\"button\">\n {{ getReplyLabel(annotation) }}\n </button>\n </footer>\n </article>\n </ng-template>\n\n @for (annotation of filteredAnnotations(); track annotation.id ?? annotation.pageNumber + '-' + $index) {\n <ng-container\n [ngTemplateOutlet]=\"getAnnotationTemplate(annotation, $index) ?? defaultAnnotationTemplate\"\n [ngTemplateOutletContext]=\"getAnnotationTemplateContext(annotation, $index)\" />\n } @empty {\n <div class=\"p-4 text-center text-sm text-on-surface-variant\">No annotations found.</div>\n }\n </div>\n </div>\n </ngs-panel-content>\n</ngs-panel>\n", styles: [":host{display:block;min-height:0;height:100%}\n"], dependencies: [{ kind: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "directive", type: IconButtonSuffix, selector: "[ngsIconButtonSuffix]" }, { kind: "directive", type: IconPrefix, selector: "[ngsIconPrefix]" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Panel, selector: "ngs-panel", inputs: ["absolute"], exportAs: ["ngsPanel"] }, { kind: "component", type: PanelContent, selector: "ngs-panel-content", exportAs: ["ngsPanelContent"] }, { kind: "component", type: PanelHeader, selector: "ngs-panel-header", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelHeader"] }, { kind: "component", type: Toolbar, selector: "ngs-toolbar", exportAs: ["ngsToolbar"] }, { kind: "component", type: ToolbarItem, selector: "ngs-toolbar-item", inputs: ["hidden"], outputs: ["hiddenChange"] }, { kind: "component", type: ToolbarSpacer, selector: "ngs-toolbar-spacer" }, { kind: "component", type: ToolbarTitle, selector: "ngs-toolbar-title" }, { kind: "component", type: PanelSubheader, selector: "ngs-panel-subheader", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelSubheader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
93
|
+
}
|
|
94
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotations, decorators: [{
|
|
95
|
+
type: Component,
|
|
96
|
+
args: [{ selector: 'ngs-pdf-viewer-annotations', standalone: true, imports: [
|
|
97
|
+
Button,
|
|
98
|
+
FormField,
|
|
99
|
+
Icon,
|
|
100
|
+
IconButtonSuffix,
|
|
101
|
+
IconPrefix,
|
|
102
|
+
Input,
|
|
103
|
+
NgTemplateOutlet,
|
|
104
|
+
Panel,
|
|
105
|
+
PanelContent,
|
|
106
|
+
PanelHeader,
|
|
107
|
+
Toolbar,
|
|
108
|
+
ToolbarItem,
|
|
109
|
+
ToolbarSpacer,
|
|
110
|
+
ToolbarTitle,
|
|
111
|
+
PanelSubheader,
|
|
112
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
113
|
+
class: 'ngs-pdf-viewer-annotations',
|
|
114
|
+
}, template: "<ngs-panel class=\"pdf-viewer-annotations bg-surface\" aria-label=\"PDF annotations\">\n <ngs-panel-header class=\"p-0\">\n <ngs-toolbar class=\"w-full py-0 pr-2 ps-4\" aria-label=\"PDF annotations toolbar\">\n <ngs-toolbar-title>\n Annotations\n </ngs-toolbar-title>\n <ngs-toolbar-spacer />\n <ngs-toolbar-item>\n <button\n class=\"pdf-viewer-annotations__close\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Close annotations panel\"\n (click)=\"closed.emit()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n </ngs-toolbar-item>\n </ngs-toolbar>\n </ngs-panel-header>\n\n <ngs-panel-subheader autoHeight class=\"px-4 pb-4\">\n <ngs-form-field subscriptHiddenIfEmpty>\n <ngs-icon ngsIconPrefix name=\"fluent:search-24-regular\" />\n <input\n ngsInput\n type=\"text\"\n aria-label=\"Filter annotations\"\n placeholder=\"Filter annotations...\"\n [value]=\"filterQuery()\"\n (input)=\"setFilterQuery($event)\" />\n @if (filterQuery()) {\n <button\n ngsIconButton\n ngsIconButtonSuffix\n type=\"button\"\n aria-label=\"Clear annotation filter\"\n (click)=\"clearFilterQuery()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n }\n </ngs-form-field>\n </ngs-panel-subheader>\n\n <ngs-panel-content>\n <div class=\"flex min-h-full flex-col\">\n <div class=\"flex flex-1 flex-col gap-4 pb-4 px-4\">\n <ng-template #defaultAnnotationTemplate let-annotation let-goToPage=\"goToPage\">\n <article class=\"rounded-2xl border border-border bg-surface p-4 shadow-xs\">\n <header class=\"flex items-start gap-3\">\n <span class=\"flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary-container text-xs font-semibold text-on-primary-container\">\n @if (getAvatarImage(annotation); as avatarImage) {\n <img\n class=\"h-full w-full object-cover\"\n [src]=\"avatarImage\"\n [alt]=\"annotation.author\" />\n } @else {\n {{ getAvatarLabel(annotation) }}\n }\n </span>\n\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-sm font-semibold text-on-surface\">{{ annotation.author }}</span>\n @if (annotation.time) {\n <span class=\"block truncate text-xs text-on-surface-variant\">{{ annotation.time }}</span>\n }\n </span>\n\n <span class=\"rounded-full bg-surface-container-high px-2 py-1 text-xs font-medium tracking-normal text-on-surface-variant uppercase\">\n {{ getAnnotationTypeLabel(annotation) }}\n </span>\n </header>\n\n <p class=\"mt-3 mb-0 text-base leading-relaxed text-on-surface\">{{ annotation.text }}</p>\n\n <footer class=\"mt-4 flex items-center justify-between gap-2\">\n <button\n ngsButton=\"tonal\"\n type=\"button\"\n (click)=\"goToPage(annotation.pageNumber)\">\n Page {{ annotation.pageNumber }}\n </button>\n\n <button ngsButton=\"text\" type=\"button\">\n {{ getReplyLabel(annotation) }}\n </button>\n </footer>\n </article>\n </ng-template>\n\n @for (annotation of filteredAnnotations(); track annotation.id ?? annotation.pageNumber + '-' + $index) {\n <ng-container\n [ngTemplateOutlet]=\"getAnnotationTemplate(annotation, $index) ?? defaultAnnotationTemplate\"\n [ngTemplateOutletContext]=\"getAnnotationTemplateContext(annotation, $index)\" />\n } @empty {\n <div class=\"p-4 text-center text-sm text-on-surface-variant\">No annotations found.</div>\n }\n </div>\n </div>\n </ngs-panel-content>\n</ngs-panel>\n", styles: [":host{display:block;min-height:0;height:100%}\n"] }]
|
|
115
|
+
}], propDecorators: { annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }], annotationDefs: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotationDefs", required: false }] }], annotationTypeProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotationTypeProperty", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], pageSelected: [{ type: i0.Output, args: ["pageSelected"] }] } });
|
|
116
|
+
|
|
117
|
+
class PdfViewerAnnotationDef {
|
|
118
|
+
template;
|
|
119
|
+
annotationWhen = input(undefined, { ...(ngDevMode ? { debugName: "annotationWhen" } : /* istanbul ignore next */ {}), alias: 'ngsPdfViewerAnnotation' });
|
|
120
|
+
defWhen = input(undefined, { ...(ngDevMode ? { debugName: "defWhen" } : /* istanbul ignore next */ {}), alias: 'ngsPdfViewerAnnotationDef' });
|
|
121
|
+
when = input(undefined, { ...(ngDevMode ? { debugName: "when" } : /* istanbul ignore next */ {}), alias: 'ngsPdfViewerAnnotationWhen' });
|
|
122
|
+
constructor(template) {
|
|
123
|
+
this.template = template;
|
|
124
|
+
}
|
|
125
|
+
matches(annotation, index, typeProperty) {
|
|
126
|
+
const whenValue = this.whenValue();
|
|
127
|
+
if (!whenValue) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (typeof whenValue === 'function') {
|
|
131
|
+
return whenValue(annotation, index);
|
|
132
|
+
}
|
|
133
|
+
return annotation[typeProperty] === whenValue;
|
|
134
|
+
}
|
|
135
|
+
hasWhen() {
|
|
136
|
+
return !!this.whenValue();
|
|
137
|
+
}
|
|
138
|
+
whenValue() {
|
|
139
|
+
return this.when() ?? this.defWhen() ?? this.annotationWhen();
|
|
140
|
+
}
|
|
141
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotationDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
142
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.4", type: PdfViewerAnnotationDef, isStandalone: true, selector: "[ngsPdfViewerAnnotationDef], [ngsPdfViewerAnnotation]", inputs: { annotationWhen: { classPropertyName: "annotationWhen", publicName: "ngsPdfViewerAnnotation", isSignal: true, isRequired: false, transformFunction: null }, defWhen: { classPropertyName: "defWhen", publicName: "ngsPdfViewerAnnotationDef", isSignal: true, isRequired: false, transformFunction: null }, when: { classPropertyName: "when", publicName: "ngsPdfViewerAnnotationWhen", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
143
|
+
}
|
|
144
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotationDef, decorators: [{
|
|
145
|
+
type: Directive,
|
|
146
|
+
args: [{
|
|
147
|
+
selector: '[ngsPdfViewerAnnotationDef], [ngsPdfViewerAnnotation]',
|
|
148
|
+
standalone: true,
|
|
149
|
+
}]
|
|
150
|
+
}], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { annotationWhen: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsPdfViewerAnnotation", required: false }] }], defWhen: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsPdfViewerAnnotationDef", required: false }] }], when: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngsPdfViewerAnnotationWhen", required: false }] }] } });
|
|
11
151
|
|
|
12
152
|
class PdfViewerEngineService {
|
|
13
153
|
engines = new Map();
|
|
@@ -16,7 +156,10 @@ class PdfViewerEngineService {
|
|
|
16
156
|
if (cachedEngine) {
|
|
17
157
|
return cachedEngine;
|
|
18
158
|
}
|
|
19
|
-
const engine = this.createEngine(wasmUrl)
|
|
159
|
+
const engine = this.createEngine(wasmUrl).catch((error) => {
|
|
160
|
+
this.engines.delete(wasmUrl);
|
|
161
|
+
throw error;
|
|
162
|
+
});
|
|
20
163
|
this.engines.set(wasmUrl, engine);
|
|
21
164
|
return engine;
|
|
22
165
|
}
|
|
@@ -38,6 +181,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
38
181
|
}]
|
|
39
182
|
}] });
|
|
40
183
|
|
|
184
|
+
class PdfViewerSearch {
|
|
185
|
+
results = input([], ...(ngDevMode ? [{ debugName: "results" }] : /* istanbul ignore next */ []));
|
|
186
|
+
query = input('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
187
|
+
closed = output();
|
|
188
|
+
resultSelected = output();
|
|
189
|
+
searchChanged = output();
|
|
190
|
+
caseSensitive = signal(false, ...(ngDevMode ? [{ debugName: "caseSensitive" }] : /* istanbul ignore next */ []));
|
|
191
|
+
wholeWord = signal(false, ...(ngDevMode ? [{ debugName: "wholeWord" }] : /* istanbul ignore next */ []));
|
|
192
|
+
activeResultIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeResultIndex" }] : /* istanbul ignore next */ []));
|
|
193
|
+
queryValue = signal('', ...(ngDevMode ? [{ debugName: "queryValue" }] : /* istanbul ignore next */ []));
|
|
194
|
+
hasQuery = computed(() => this.queryValue().trim().length > 0, ...(ngDevMode ? [{ debugName: "hasQuery" }] : /* istanbul ignore next */ []));
|
|
195
|
+
visibleResultGroups = computed(() => {
|
|
196
|
+
const groups = new Map();
|
|
197
|
+
this.results().forEach((result, index) => {
|
|
198
|
+
const group = groups.get(result.pageNumber) ?? {
|
|
199
|
+
pageNumber: result.pageNumber,
|
|
200
|
+
results: [],
|
|
201
|
+
};
|
|
202
|
+
group.results.push({ result, index });
|
|
203
|
+
groups.set(result.pageNumber, group);
|
|
204
|
+
});
|
|
205
|
+
return [...groups.values()];
|
|
206
|
+
}, ...(ngDevMode ? [{ debugName: "visibleResultGroups" }] : /* istanbul ignore next */ []));
|
|
207
|
+
resultCountLabel = computed(() => {
|
|
208
|
+
const count = this.results().length;
|
|
209
|
+
return `${count} ${count === 1 ? 'result' : 'results'} found`;
|
|
210
|
+
}, ...(ngDevMode ? [{ debugName: "resultCountLabel" }] : /* istanbul ignore next */ []));
|
|
211
|
+
constructor() {
|
|
212
|
+
effect(() => {
|
|
213
|
+
const query = this.query();
|
|
214
|
+
untracked(() => {
|
|
215
|
+
this.queryValue.set(query);
|
|
216
|
+
this.activeResultIndex.set(0);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
setQuery(event) {
|
|
221
|
+
this.queryValue.set(event.target.value);
|
|
222
|
+
this.activeResultIndex.set(0);
|
|
223
|
+
this.emitSearchChanged();
|
|
224
|
+
}
|
|
225
|
+
clearQuery() {
|
|
226
|
+
this.queryValue.set('');
|
|
227
|
+
this.activeResultIndex.set(0);
|
|
228
|
+
this.emitSearchChanged();
|
|
229
|
+
}
|
|
230
|
+
setCaseSensitive(value) {
|
|
231
|
+
this.caseSensitive.set(value);
|
|
232
|
+
this.activeResultIndex.set(0);
|
|
233
|
+
this.emitSearchChanged();
|
|
234
|
+
}
|
|
235
|
+
setWholeWord(value) {
|
|
236
|
+
this.wholeWord.set(value);
|
|
237
|
+
this.activeResultIndex.set(0);
|
|
238
|
+
this.emitSearchChanged();
|
|
239
|
+
}
|
|
240
|
+
previousResult() {
|
|
241
|
+
const count = this.results().length;
|
|
242
|
+
if (count === 0) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.activeResultIndex.update((index) => (index - 1 + count) % count);
|
|
246
|
+
}
|
|
247
|
+
nextResult() {
|
|
248
|
+
const count = this.results().length;
|
|
249
|
+
if (count === 0) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
this.activeResultIndex.update((index) => (index + 1) % count);
|
|
253
|
+
}
|
|
254
|
+
selectResult(result, index) {
|
|
255
|
+
this.activeResultIndex.set(index);
|
|
256
|
+
this.resultSelected.emit(result);
|
|
257
|
+
}
|
|
258
|
+
resultParts(result) {
|
|
259
|
+
const query = this.queryValue().trim();
|
|
260
|
+
if (!query) {
|
|
261
|
+
return [{ text: result.excerpt, highlight: false }];
|
|
262
|
+
}
|
|
263
|
+
const flags = this.caseSensitive() ? 'g' : 'gi';
|
|
264
|
+
const escapedQuery = this.escapeRegExp(query);
|
|
265
|
+
const pattern = this.wholeWord()
|
|
266
|
+
? new RegExp(`\\b${escapedQuery}\\b`, flags)
|
|
267
|
+
: new RegExp(escapedQuery, flags);
|
|
268
|
+
const parts = [];
|
|
269
|
+
let lastIndex = 0;
|
|
270
|
+
let match;
|
|
271
|
+
while ((match = pattern.exec(result.excerpt)) !== null) {
|
|
272
|
+
if (match.index > lastIndex) {
|
|
273
|
+
parts.push({ text: result.excerpt.slice(lastIndex, match.index), highlight: false });
|
|
274
|
+
}
|
|
275
|
+
parts.push({ text: match[0], highlight: true });
|
|
276
|
+
lastIndex = match.index + match[0].length;
|
|
277
|
+
if (match[0].length === 0) {
|
|
278
|
+
pattern.lastIndex++;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (lastIndex < result.excerpt.length) {
|
|
282
|
+
parts.push({ text: result.excerpt.slice(lastIndex), highlight: false });
|
|
283
|
+
}
|
|
284
|
+
return parts.length > 0 ? parts : [{ text: result.excerpt, highlight: false }];
|
|
285
|
+
}
|
|
286
|
+
emitSearchChanged() {
|
|
287
|
+
this.searchChanged.emit({
|
|
288
|
+
query: this.queryValue(),
|
|
289
|
+
options: {
|
|
290
|
+
caseSensitive: this.caseSensitive(),
|
|
291
|
+
wholeWord: this.wholeWord(),
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
escapeRegExp(value) {
|
|
296
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
297
|
+
}
|
|
298
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
299
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PdfViewerSearch, isStandalone: true, selector: "ngs-pdf-viewer-search", inputs: { results: { classPropertyName: "results", publicName: "results", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", resultSelected: "resultSelected", searchChanged: "searchChanged" }, host: { classAttribute: "ngs-pdf-viewer-search" }, ngImport: i0, template: "<ngs-panel class=\"pdf-viewer-search\" aria-label=\"PDF search\">\n <ngs-panel-header class=\"ps-4 pe-3\">\n <ngs-toolbar>\n <ngs-toolbar-title>Search</ngs-toolbar-title>\n <ngs-toolbar-spacer />\n <button ngsIconButton type=\"button\" aria-label=\"Close search panel\" (click)=\"closed.emit()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n </ngs-toolbar>\n </ngs-panel-header>\n\n <ngs-panel-subheader autoHeight class=\"px-4 pb-4\">\n <ngs-form-field class=\"pdf-viewer-search__field\" subscriptHiddenIfEmpty>\n <ngs-icon name=\"fluent:search-24-regular\" ngsIconPrefix />\n <input\n ngsInput\n type=\"text\"\n aria-label=\"Search PDF\"\n placeholder=\"Search\"\n [value]=\"queryValue()\"\n (input)=\"setQuery($event)\" />\n @if (queryValue()) {\n <button ngsIconButtonSuffix ngsIconButton type=\"button\" aria-label=\"Clear search\" (click)=\"clearQuery()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n }\n </ngs-form-field>\n\n <div class=\"pdf-viewer-search__options mt-4 grid gap-3 text-base text-on-surface\">\n <ngs-checkbox [checked]=\"caseSensitive()\" (checkedChange)=\"setCaseSensitive($event)\">\n Case sensitive\n </ngs-checkbox>\n <ngs-checkbox [checked]=\"wholeWord()\" (checkedChange)=\"setWholeWord($event)\">\n Whole word\n </ngs-checkbox>\n </div>\n\n @if (hasQuery()) {\n <div class=\"pdf-viewer-search__summary mt-4 flex items-center justify-between gap-4 text-base text-on-surface-variant\">\n <span>{{ resultCountLabel() }}</span>\n <div class=\"pdf-viewer-search__nav flex items-center gap-3\">\n <button\n class=\"text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Previous search result\"\n [disabled]=\"results().length === 0\"\n (click)=\"previousResult()\">\n <ngs-icon name=\"fluent:chevron-left-24-regular\" />\n </button>\n <button\n class=\"text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Next search result\"\n [disabled]=\"results().length === 0\"\n (click)=\"nextResult()\">\n <ngs-icon name=\"fluent:chevron-right-24-regular\" />\n </button>\n </div>\n </div>\n }\n </ngs-panel-subheader>\n\n <ngs-panel-content>\n <div class=\"pdf-viewer-search__content flex min-h-full flex-col gap-4 px-4 pb-4\">\n @if (hasQuery()) {\n <div class=\"pdf-viewer-search__results grid min-h-0 gap-4\">\n @for (group of visibleResultGroups(); track group.pageNumber) {\n <section class=\"pdf-viewer-search__page-group grid gap-3 bg-surface\">\n <h3 class=\"m-0 text-base font-medium text-on-surface-variant\">Page {{ group.pageNumber }}</h3>\n @for (item of group.results; track item.result.id ?? item.result.pageNumber + '-' + item.index) {\n <button\n class=\"pdf-viewer-search__result w-full cursor-pointer rounded-2xl border border-border p-4 text-left text-base leading-snug text-on-surface\"\n type=\"button\"\n [class.bg-primary-container]=\"item.index === activeResultIndex()\"\n [class.bg-surface]=\"item.index !== activeResultIndex()\"\n (click)=\"selectResult(item.result, item.index)\">\n @for (part of resultParts(item.result); track part.text + '-' + $index) {\n <span\n [class.font-bold]=\"part.highlight\"\n [class.text-primary]=\"part.highlight\">{{ part.text }}</span>\n }\n </button>\n }\n </section>\n } @empty {\n <div class=\"pdf-viewer-search__empty p-4 text-center text-sm text-on-surface-variant\">No results.</div>\n }\n </div>\n }\n </div>\n </ngs-panel-content>\n</ngs-panel>\n", styles: [":host{display:block;min-height:0;height:100%}\n"], dependencies: [{ kind: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: Checkbox, selector: "ngs-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["checkedChange", "disabledChange", "indeterminateChange", "change"], exportAs: ["ngsCheckbox"] }, { kind: "component", type: FormField, selector: "ngs-form-field", inputs: ["subscriptHiddenIfEmpty", "sameHeightAsButton"], exportAs: ["ngsFormField"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "directive", type: IconButtonSuffix, selector: "[ngsIconButtonSuffix]" }, { kind: "directive", type: IconPrefix, selector: "[ngsIconPrefix]" }, { kind: "directive", type: Input, selector: "input[ngsInput], textarea[ngsInput]", inputs: ["id", "placeholder", "required", "disabled", "readonly", "errorStateMatcher"], exportAs: ["ngsInput"] }, { kind: "component", type: Panel, selector: "ngs-panel", inputs: ["absolute"], exportAs: ["ngsPanel"] }, { kind: "component", type: PanelContent, selector: "ngs-panel-content", exportAs: ["ngsPanelContent"] }, { kind: "component", type: PanelHeader, selector: "ngs-panel-header", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelHeader"] }, { kind: "component", type: Toolbar, selector: "ngs-toolbar", exportAs: ["ngsToolbar"] }, { kind: "component", type: ToolbarSpacer, selector: "ngs-toolbar-spacer" }, { kind: "component", type: ToolbarTitle, selector: "ngs-toolbar-title" }, { kind: "component", type: PanelSubheader, selector: "ngs-panel-subheader", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelSubheader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
300
|
+
}
|
|
301
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerSearch, decorators: [{
|
|
302
|
+
type: Component,
|
|
303
|
+
args: [{ selector: 'ngs-pdf-viewer-search', standalone: true, imports: [
|
|
304
|
+
Button,
|
|
305
|
+
Checkbox,
|
|
306
|
+
FormField,
|
|
307
|
+
Icon,
|
|
308
|
+
IconButtonSuffix,
|
|
309
|
+
IconPrefix,
|
|
310
|
+
Input,
|
|
311
|
+
Panel,
|
|
312
|
+
PanelContent,
|
|
313
|
+
PanelHeader,
|
|
314
|
+
Toolbar,
|
|
315
|
+
ToolbarSpacer,
|
|
316
|
+
ToolbarTitle,
|
|
317
|
+
PanelSubheader,
|
|
318
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
319
|
+
class: 'ngs-pdf-viewer-search',
|
|
320
|
+
}, template: "<ngs-panel class=\"pdf-viewer-search\" aria-label=\"PDF search\">\n <ngs-panel-header class=\"ps-4 pe-3\">\n <ngs-toolbar>\n <ngs-toolbar-title>Search</ngs-toolbar-title>\n <ngs-toolbar-spacer />\n <button ngsIconButton type=\"button\" aria-label=\"Close search panel\" (click)=\"closed.emit()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n </ngs-toolbar>\n </ngs-panel-header>\n\n <ngs-panel-subheader autoHeight class=\"px-4 pb-4\">\n <ngs-form-field class=\"pdf-viewer-search__field\" subscriptHiddenIfEmpty>\n <ngs-icon name=\"fluent:search-24-regular\" ngsIconPrefix />\n <input\n ngsInput\n type=\"text\"\n aria-label=\"Search PDF\"\n placeholder=\"Search\"\n [value]=\"queryValue()\"\n (input)=\"setQuery($event)\" />\n @if (queryValue()) {\n <button ngsIconButtonSuffix ngsIconButton type=\"button\" aria-label=\"Clear search\" (click)=\"clearQuery()\">\n <ngs-icon name=\"fluent:dismiss-24-regular\" />\n </button>\n }\n </ngs-form-field>\n\n <div class=\"pdf-viewer-search__options mt-4 grid gap-3 text-base text-on-surface\">\n <ngs-checkbox [checked]=\"caseSensitive()\" (checkedChange)=\"setCaseSensitive($event)\">\n Case sensitive\n </ngs-checkbox>\n <ngs-checkbox [checked]=\"wholeWord()\" (checkedChange)=\"setWholeWord($event)\">\n Whole word\n </ngs-checkbox>\n </div>\n\n @if (hasQuery()) {\n <div class=\"pdf-viewer-search__summary mt-4 flex items-center justify-between gap-4 text-base text-on-surface-variant\">\n <span>{{ resultCountLabel() }}</span>\n <div class=\"pdf-viewer-search__nav flex items-center gap-3\">\n <button\n class=\"text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Previous search result\"\n [disabled]=\"results().length === 0\"\n (click)=\"previousResult()\">\n <ngs-icon name=\"fluent:chevron-left-24-regular\" />\n </button>\n <button\n class=\"text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Next search result\"\n [disabled]=\"results().length === 0\"\n (click)=\"nextResult()\">\n <ngs-icon name=\"fluent:chevron-right-24-regular\" />\n </button>\n </div>\n </div>\n }\n </ngs-panel-subheader>\n\n <ngs-panel-content>\n <div class=\"pdf-viewer-search__content flex min-h-full flex-col gap-4 px-4 pb-4\">\n @if (hasQuery()) {\n <div class=\"pdf-viewer-search__results grid min-h-0 gap-4\">\n @for (group of visibleResultGroups(); track group.pageNumber) {\n <section class=\"pdf-viewer-search__page-group grid gap-3 bg-surface\">\n <h3 class=\"m-0 text-base font-medium text-on-surface-variant\">Page {{ group.pageNumber }}</h3>\n @for (item of group.results; track item.result.id ?? item.result.pageNumber + '-' + item.index) {\n <button\n class=\"pdf-viewer-search__result w-full cursor-pointer rounded-2xl border border-border p-4 text-left text-base leading-snug text-on-surface\"\n type=\"button\"\n [class.bg-primary-container]=\"item.index === activeResultIndex()\"\n [class.bg-surface]=\"item.index !== activeResultIndex()\"\n (click)=\"selectResult(item.result, item.index)\">\n @for (part of resultParts(item.result); track part.text + '-' + $index) {\n <span\n [class.font-bold]=\"part.highlight\"\n [class.text-primary]=\"part.highlight\">{{ part.text }}</span>\n }\n </button>\n }\n </section>\n } @empty {\n <div class=\"pdf-viewer-search__empty p-4 text-center text-sm text-on-surface-variant\">No results.</div>\n }\n </div>\n }\n </div>\n </ngs-panel-content>\n</ngs-panel>\n", styles: [":host{display:block;min-height:0;height:100%}\n"] }]
|
|
321
|
+
}], ctorParameters: () => [], propDecorators: { results: [{ type: i0.Input, args: [{ isSignal: true, alias: "results", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], resultSelected: [{ type: i0.Output, args: ["resultSelected"] }], searchChanged: [{ type: i0.Output, args: ["searchChanged"] }] } });
|
|
322
|
+
|
|
41
323
|
class PdfViewer {
|
|
42
324
|
textSelectionHorizontalPadding = 2;
|
|
43
325
|
textSelectionLineHeight = 1.2;
|
|
@@ -49,16 +331,26 @@ class PdfViewer {
|
|
|
49
331
|
isBrowser = isPlatformBrowser(this.platformId);
|
|
50
332
|
viewerBody = viewChild('viewerBody', ...(ngDevMode ? [{ debugName: "viewerBody" }] : /* istanbul ignore next */ []));
|
|
51
333
|
pageList = viewChild('pageList', ...(ngDevMode ? [{ debugName: "pageList" }] : /* istanbul ignore next */ []));
|
|
334
|
+
annotationDefs = contentChildren(PdfViewerAnnotationDef, { ...(ngDevMode ? { debugName: "annotationDefs" } : /* istanbul ignore next */ {}), descendants: true });
|
|
52
335
|
src = input(null, ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
336
|
+
documentName = input(null, ...(ngDevMode ? [{ debugName: "documentName" }] : /* istanbul ignore next */ []));
|
|
53
337
|
wasmUrl = input('/assets/embedpdf/pdfium.wasm', ...(ngDevMode ? [{ debugName: "wasmUrl" }] : /* istanbul ignore next */ []));
|
|
54
338
|
page = input(1, { ...(ngDevMode ? { debugName: "page" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
55
339
|
scale = input(1, { ...(ngDevMode ? { debugName: "scale" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
56
340
|
minScale = input(0.2, { ...(ngDevMode ? { debugName: "minScale" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
57
341
|
maxScale = input(60, { ...(ngDevMode ? { debugName: "maxScale" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
58
342
|
zoomStep = input(0.1, { ...(ngDevMode ? { debugName: "zoomStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
343
|
+
maxRenderPixels = input(128_000_000, { ...(ngDevMode ? { debugName: "maxRenderPixels" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
344
|
+
maxRenderDimension = input(13_000, { ...(ngDevMode ? { debugName: "maxRenderDimension" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
59
345
|
renderAll = input(true, { ...(ngDevMode ? { debugName: "renderAll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
60
346
|
showToolbar = input(true, { ...(ngDevMode ? { debugName: "showToolbar" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
61
347
|
showPageList = input(true, { ...(ngDevMode ? { debugName: "showPageList" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
348
|
+
showSearchPanel = input(true, { ...(ngDevMode ? { debugName: "showSearchPanel" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
349
|
+
showAnnotationsPanel = input(false, { ...(ngDevMode ? { debugName: "showAnnotationsPanel" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
350
|
+
annotations = input([], ...(ngDevMode ? [{ debugName: "annotations" }] : /* istanbul ignore next */ []));
|
|
351
|
+
annotationsDataSource = input(null, ...(ngDevMode ? [{ debugName: "annotationsDataSource" }] : /* istanbul ignore next */ []));
|
|
352
|
+
annotationTypeProperty = input('type', ...(ngDevMode ? [{ debugName: "annotationTypeProperty" }] : /* istanbul ignore next */ []));
|
|
353
|
+
searchQuery = input('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
|
|
62
354
|
withAnnotations = input(true, { ...(ngDevMode ? { debugName: "withAnnotations" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
63
355
|
withForms = input(true, { ...(ngDevMode ? { debugName: "withForms" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
64
356
|
loaded = output();
|
|
@@ -72,12 +364,22 @@ class PdfViewer {
|
|
|
72
364
|
pageCount = signal(0, ...(ngDevMode ? [{ debugName: "pageCount" }] : /* istanbul ignore next */ []));
|
|
73
365
|
activePage = signal(1, ...(ngDevMode ? [{ debugName: "activePage" }] : /* istanbul ignore next */ []));
|
|
74
366
|
zoom = signal(1, ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
75
|
-
pageListVisible = signal(
|
|
367
|
+
pageListVisible = signal(false, ...(ngDevMode ? [{ debugName: "pageListVisible" }] : /* istanbul ignore next */ []));
|
|
368
|
+
searchPanelVisible = signal(false, ...(ngDevMode ? [{ debugName: "searchPanelVisible" }] : /* istanbul ignore next */ []));
|
|
369
|
+
annotationsPanelVisible = signal(false, ...(ngDevMode ? [{ debugName: "annotationsPanelVisible" }] : /* istanbul ignore next */ []));
|
|
370
|
+
asidePanelInteractive = signal(false, ...(ngDevMode ? [{ debugName: "asidePanelInteractive" }] : /* istanbul ignore next */ []));
|
|
76
371
|
spreadMode = signal('single', ...(ngDevMode ? [{ debugName: "spreadMode" }] : /* istanbul ignore next */ []));
|
|
77
|
-
scrollLayout = signal('
|
|
372
|
+
scrollLayout = signal('vertical', ...(ngDevMode ? [{ debugName: "scrollLayout" }] : /* istanbul ignore next */ []));
|
|
373
|
+
pageRotation = signal(Rotation.Degree0, ...(ngDevMode ? [{ debugName: "pageRotation" }] : /* istanbul ignore next */ []));
|
|
374
|
+
zoomMode = signal('custom', ...(ngDevMode ? [{ debugName: "zoomMode" }] : /* istanbul ignore next */ []));
|
|
375
|
+
annotationItems = signal([], ...(ngDevMode ? [{ debugName: "annotationItems" }] : /* istanbul ignore next */ []));
|
|
376
|
+
activeSearchQuery = signal('', ...(ngDevMode ? [{ debugName: "activeSearchQuery" }] : /* istanbul ignore next */ []));
|
|
377
|
+
pdfSearchResults = signal([], ...(ngDevMode ? [{ debugName: "pdfSearchResults" }] : /* istanbul ignore next */ []));
|
|
78
378
|
selectionRects = signal([], ...(ngDevMode ? [{ debugName: "selectionRects" }] : /* istanbul ignore next */ []));
|
|
79
379
|
hasDocument = computed(() => this.pageCount() > 0, ...(ngDevMode ? [{ debugName: "hasDocument" }] : /* istanbul ignore next */ []));
|
|
80
|
-
isPageListVisible = computed(() => this.showPageList() && this.
|
|
380
|
+
isPageListVisible = computed(() => this.showPageList() && this.pageListVisible(), ...(ngDevMode ? [{ debugName: "isPageListVisible" }] : /* istanbul ignore next */ []));
|
|
381
|
+
isSearchPanelVisible = computed(() => this.showSearchPanel() && this.searchPanelVisible(), ...(ngDevMode ? [{ debugName: "isSearchPanelVisible" }] : /* istanbul ignore next */ []));
|
|
382
|
+
isAnnotationsPanelVisible = computed(() => this.showAnnotationsPanel() && !this.searchPanelVisible() && this.annotationsPanelVisible(), ...(ngDevMode ? [{ debugName: "isAnnotationsPanelVisible" }] : /* istanbul ignore next */ []));
|
|
81
383
|
thumbnailPageMap = computed(() => new Map(this.thumbnailPages().map((thumbnail) => [thumbnail.pageNumber, thumbnail])), ...(ngDevMode ? [{ debugName: "thumbnailPageMap" }] : /* istanbul ignore next */ []));
|
|
82
384
|
pageItems = computed(() => Array.from({ length: this.pageCount() }, (_, index) => {
|
|
83
385
|
const pageNumber = index + 1;
|
|
@@ -91,6 +393,9 @@ class PdfViewer {
|
|
|
91
393
|
canZoomOut = computed(() => this.zoom() > this.getScaleBounds().min, ...(ngDevMode ? [{ debugName: "canZoomOut" }] : /* istanbul ignore next */ []));
|
|
92
394
|
canZoomIn = computed(() => this.zoom() < this.getScaleBounds().max, ...(ngDevMode ? [{ debugName: "canZoomIn" }] : /* istanbul ignore next */ []));
|
|
93
395
|
zoomLabel = computed(() => `${Math.round(this.zoom() * 100)}%`, ...(ngDevMode ? [{ debugName: "zoomLabel" }] : /* istanbul ignore next */ []));
|
|
396
|
+
displayDocumentName = computed(() => this.documentName() || this.getSourceName(this.src()), ...(ngDevMode ? [{ debugName: "displayDocumentName" }] : /* istanbul ignore next */ []));
|
|
397
|
+
pageSpreads = computed(() => this.groupPagesIntoSpreads(this.renderedPages(), this.spreadMode()), ...(ngDevMode ? [{ debugName: "pageSpreads" }] : /* istanbul ignore next */ []));
|
|
398
|
+
zoomPresets = [0.25, 0.5, 1, 1.25, 1.5, 2, 4, 8, 16];
|
|
94
399
|
engine = null;
|
|
95
400
|
pdfDocument = null;
|
|
96
401
|
registry = null;
|
|
@@ -98,21 +403,36 @@ class PdfViewer {
|
|
|
98
403
|
visiblePageRatios = new Map();
|
|
99
404
|
loadToken = 0;
|
|
100
405
|
renderToken = 0;
|
|
406
|
+
searchToken = 0;
|
|
101
407
|
scrollSyncFrame = null;
|
|
102
408
|
programmaticScrollTargetPage = null;
|
|
103
409
|
programmaticScrollTimeout = null;
|
|
104
410
|
pageObserverFrame = null;
|
|
105
411
|
pageObserverTimeout = null;
|
|
106
412
|
visiblePageRenderFrame = null;
|
|
413
|
+
visiblePageRenderTimeout = null;
|
|
107
414
|
pageObserverRefreshAttempts = 0;
|
|
108
415
|
selectionStart = null;
|
|
109
416
|
isViewInitialized = false;
|
|
417
|
+
annotationDataSourceToken = 0;
|
|
418
|
+
annotationDataSourceCleanup = null;
|
|
419
|
+
lastZoomChangeTime = 0;
|
|
110
420
|
programmaticScrollMinDuration = 900;
|
|
111
421
|
programmaticScrollMaxDuration = 6000;
|
|
422
|
+
qualityRenderZoomIdleDelay = 160;
|
|
423
|
+
documentOpenTimeoutMs = 15000;
|
|
112
424
|
constructor() {
|
|
425
|
+
afterNextRender(() => {
|
|
426
|
+
if (this.isBrowser) {
|
|
427
|
+
this.asidePanelInteractive.set(true);
|
|
428
|
+
}
|
|
429
|
+
});
|
|
113
430
|
this.destroyRef.onDestroy(() => {
|
|
114
431
|
this.loadToken++;
|
|
115
432
|
this.renderToken++;
|
|
433
|
+
this.searchToken++;
|
|
434
|
+
this.annotationDataSourceToken++;
|
|
435
|
+
this.annotationDataSourceCleanup?.();
|
|
116
436
|
this.cancelScrollSyncFrame();
|
|
117
437
|
this.clearProgrammaticScrollLock();
|
|
118
438
|
this.cancelPageObserverFrame();
|
|
@@ -123,6 +443,25 @@ class PdfViewer {
|
|
|
123
443
|
void this.closeDocument();
|
|
124
444
|
void this.registry?.destroy();
|
|
125
445
|
});
|
|
446
|
+
effect((onCleanup) => {
|
|
447
|
+
const dataSource = this.annotationsDataSource();
|
|
448
|
+
const fallbackAnnotations = this.annotations();
|
|
449
|
+
const source = this.src();
|
|
450
|
+
const documentName = this.documentName() || this.getSourceName(source);
|
|
451
|
+
const pageCount = this.pageCount();
|
|
452
|
+
const token = ++this.annotationDataSourceToken;
|
|
453
|
+
const context = {
|
|
454
|
+
source,
|
|
455
|
+
documentName,
|
|
456
|
+
pageCount,
|
|
457
|
+
};
|
|
458
|
+
this.annotationDataSourceCleanup?.();
|
|
459
|
+
this.annotationDataSourceCleanup = untracked(() => this.loadAnnotationsDataSource(dataSource ?? fallbackAnnotations, context, token));
|
|
460
|
+
onCleanup(() => {
|
|
461
|
+
this.annotationDataSourceCleanup?.();
|
|
462
|
+
this.annotationDataSourceCleanup = null;
|
|
463
|
+
});
|
|
464
|
+
});
|
|
126
465
|
effect(() => {
|
|
127
466
|
const source = this.src();
|
|
128
467
|
const wasmUrl = this.wasmUrl();
|
|
@@ -145,17 +484,33 @@ class PdfViewer {
|
|
|
145
484
|
this.activePage.set(requestedPage);
|
|
146
485
|
}
|
|
147
486
|
});
|
|
487
|
+
effect(() => {
|
|
488
|
+
const query = this.searchQuery();
|
|
489
|
+
if (query !== untracked(() => this.activeSearchQuery())) {
|
|
490
|
+
this.activeSearchQuery.set(query);
|
|
491
|
+
untracked(() => {
|
|
492
|
+
void this.searchPdf(query, {
|
|
493
|
+
caseSensitive: false,
|
|
494
|
+
wholeWord: false,
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
});
|
|
148
499
|
effect(() => {
|
|
149
500
|
const zoom = this.zoom();
|
|
150
501
|
const renderAll = this.renderAll();
|
|
151
502
|
const activePage = renderAll ? untracked(() => this.activePage()) : this.activePage();
|
|
152
503
|
const withAnnotations = this.withAnnotations();
|
|
153
504
|
const withForms = this.withForms();
|
|
505
|
+
this.pageRotation();
|
|
506
|
+
this.spreadMode();
|
|
507
|
+
this.scrollLayout();
|
|
154
508
|
if (!this.pdfDocument || !this.engine || this.isLoading()) {
|
|
155
509
|
return;
|
|
156
510
|
}
|
|
157
511
|
untracked(() => {
|
|
158
512
|
this.applyInstantZoom(zoom);
|
|
513
|
+
this.schedulePageObserverRefresh();
|
|
159
514
|
this.scheduleVisiblePagesRender({ zoom, activePage, renderAll, withAnnotations, withForms });
|
|
160
515
|
});
|
|
161
516
|
});
|
|
@@ -163,6 +518,7 @@ class PdfViewer {
|
|
|
163
518
|
const withAnnotations = this.withAnnotations();
|
|
164
519
|
const isLoading = this.isLoading();
|
|
165
520
|
const pageCount = this.pageCount();
|
|
521
|
+
this.pageRotation();
|
|
166
522
|
if (isLoading || pageCount === 0 || !this.pdfDocument || !this.engine) {
|
|
167
523
|
return;
|
|
168
524
|
}
|
|
@@ -190,25 +546,134 @@ class PdfViewer {
|
|
|
190
546
|
this.setPage(this.activePage() + 1);
|
|
191
547
|
}
|
|
192
548
|
zoomIn() {
|
|
549
|
+
this.zoomMode.set('custom');
|
|
193
550
|
this.setZoom(this.roundZoom(this.zoom() + this.sanitizeZoomStep()));
|
|
194
551
|
}
|
|
195
552
|
zoomOut() {
|
|
553
|
+
this.zoomMode.set('custom');
|
|
196
554
|
this.setZoom(this.roundZoom(this.zoom() - this.sanitizeZoomStep()));
|
|
197
555
|
}
|
|
198
556
|
togglePageList() {
|
|
199
557
|
this.pageListVisible.update((isVisible) => !isVisible);
|
|
200
558
|
}
|
|
559
|
+
toggleSearchPanel() {
|
|
560
|
+
const nextVisible = !this.searchPanelVisible();
|
|
561
|
+
this.searchPanelVisible.set(nextVisible);
|
|
562
|
+
if (nextVisible) {
|
|
563
|
+
this.annotationsPanelVisible.set(false);
|
|
564
|
+
const query = this.searchQuery();
|
|
565
|
+
this.activeSearchQuery.set(query);
|
|
566
|
+
void this.searchPdf(query, {
|
|
567
|
+
caseSensitive: false,
|
|
568
|
+
wholeWord: false,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
toggleAnnotationsPanel() {
|
|
573
|
+
const nextVisible = !this.annotationsPanelVisible();
|
|
574
|
+
this.annotationsPanelVisible.set(nextVisible);
|
|
575
|
+
if (nextVisible) {
|
|
576
|
+
this.searchPanelVisible.set(false);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
closeAsidePanel() {
|
|
580
|
+
this.searchPanelVisible.set(false);
|
|
581
|
+
this.annotationsPanelVisible.set(false);
|
|
582
|
+
}
|
|
583
|
+
updatePdfSearch(event) {
|
|
584
|
+
this.activeSearchQuery.set(event.query);
|
|
585
|
+
return this.searchPdf(event.query, event.options);
|
|
586
|
+
}
|
|
587
|
+
selectSearchResult(result) {
|
|
588
|
+
this.setPage(result.pageNumber);
|
|
589
|
+
}
|
|
590
|
+
setZoomPreset(scale) {
|
|
591
|
+
this.zoomMode.set('custom');
|
|
592
|
+
this.setZoom(scale);
|
|
593
|
+
}
|
|
594
|
+
isZoomPresetSelected(scale) {
|
|
595
|
+
return this.zoomMode() === 'custom' && Math.abs(this.zoom() - this.sanitizeScale(scale)) < 0.0001;
|
|
596
|
+
}
|
|
597
|
+
fitToPage() {
|
|
598
|
+
const scale = this.getFitScale('fit-page');
|
|
599
|
+
if (scale === null) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
this.zoomMode.set('fit-page');
|
|
603
|
+
this.setZoom(scale);
|
|
604
|
+
}
|
|
605
|
+
fitToWidth() {
|
|
606
|
+
const scale = this.getFitScale('fit-width');
|
|
607
|
+
if (scale === null) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
this.zoomMode.set('fit-width');
|
|
611
|
+
this.setZoom(scale);
|
|
612
|
+
}
|
|
201
613
|
setSpreadMode(mode) {
|
|
202
614
|
this.spreadMode.set(mode);
|
|
615
|
+
this.refreshLayoutAfterModeChange();
|
|
203
616
|
}
|
|
204
617
|
setScrollLayout(layout) {
|
|
205
618
|
this.scrollLayout.set(layout);
|
|
619
|
+
this.refreshLayoutAfterModeChange();
|
|
206
620
|
}
|
|
207
621
|
rotateClockwise() {
|
|
208
|
-
this.
|
|
622
|
+
this.setPageRotation(this.rotatePageBy(1));
|
|
209
623
|
}
|
|
210
624
|
rotateCounterClockwise() {
|
|
211
|
-
this.
|
|
625
|
+
this.setPageRotation(this.rotatePageBy(-1));
|
|
626
|
+
}
|
|
627
|
+
groupPagesIntoSpreads(pages, mode) {
|
|
628
|
+
if (mode === 'single') {
|
|
629
|
+
return pages.map((page) => ({
|
|
630
|
+
id: `single-${page.pageNumber}`,
|
|
631
|
+
leadingPlaceholder: false,
|
|
632
|
+
leadingPlaceholderPage: null,
|
|
633
|
+
pages: [page],
|
|
634
|
+
}));
|
|
635
|
+
}
|
|
636
|
+
const spreads = [];
|
|
637
|
+
let pageIndex = 0;
|
|
638
|
+
if (mode === 'two-even' && pages.length > 0) {
|
|
639
|
+
spreads.push({
|
|
640
|
+
id: 'two-even-cover',
|
|
641
|
+
leadingPlaceholder: true,
|
|
642
|
+
leadingPlaceholderPage: pages[0],
|
|
643
|
+
pages: [pages[0]],
|
|
644
|
+
});
|
|
645
|
+
pageIndex = 1;
|
|
646
|
+
}
|
|
647
|
+
while (pageIndex < pages.length) {
|
|
648
|
+
const spreadPages = pages.slice(pageIndex, pageIndex + 2);
|
|
649
|
+
spreads.push({
|
|
650
|
+
id: `${mode}-${spreadPages.map((page) => page.pageNumber).join('-')}`,
|
|
651
|
+
leadingPlaceholder: false,
|
|
652
|
+
leadingPlaceholderPage: null,
|
|
653
|
+
pages: spreadPages,
|
|
654
|
+
});
|
|
655
|
+
pageIndex += 2;
|
|
656
|
+
}
|
|
657
|
+
return spreads;
|
|
658
|
+
}
|
|
659
|
+
setPageRotation(rotation) {
|
|
660
|
+
this.pageRotation.set(rotation);
|
|
661
|
+
this.applyInstantZoom(this.zoom());
|
|
662
|
+
this.refreshLayoutAfterModeChange();
|
|
663
|
+
}
|
|
664
|
+
rotatePageBy(delta) {
|
|
665
|
+
return this.normalizeRotation(this.pageRotation() + delta);
|
|
666
|
+
}
|
|
667
|
+
refreshLayoutAfterModeChange() {
|
|
668
|
+
this.selectionStart = null;
|
|
669
|
+
this.selectionRects.set([]);
|
|
670
|
+
this.schedulePageObserverRefresh();
|
|
671
|
+
const targetWindow = this.document.defaultView;
|
|
672
|
+
if (!targetWindow) {
|
|
673
|
+
this.scrollToPage(this.activePage());
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
targetWindow.requestAnimationFrame(() => this.scrollToPage(this.activePage()));
|
|
212
677
|
}
|
|
213
678
|
toggleFullscreen() {
|
|
214
679
|
if (!this.isBrowser) {
|
|
@@ -252,12 +717,31 @@ class PdfViewer {
|
|
|
252
717
|
this.scheduleCurrentVisiblePagesRender();
|
|
253
718
|
}) ?? null;
|
|
254
719
|
}
|
|
720
|
+
onViewerWheel(event) {
|
|
721
|
+
if (!event.metaKey && !event.ctrlKey) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (!this.pdfDocument || this.isLoading()) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
event.preventDefault();
|
|
728
|
+
event.stopPropagation();
|
|
729
|
+
const container = this.viewerBody()?.nativeElement;
|
|
730
|
+
const anchor = container ? this.getZoomAnchor(container, event) : null;
|
|
731
|
+
const delta = event.deltaY || event.deltaX;
|
|
732
|
+
const direction = delta < 0 ? 1 : -1;
|
|
733
|
+
const multiplier = Math.max(1, Math.min(6, Math.abs(delta) / 100));
|
|
734
|
+
const nextZoom = this.roundZoom(this.zoom() + direction * this.sanitizeZoomStep() * multiplier);
|
|
735
|
+
if (nextZoom === this.zoom()) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
this.zoomMode.set('custom');
|
|
739
|
+
this.setZoom(nextZoom);
|
|
740
|
+
this.restoreZoomAnchor(anchor);
|
|
741
|
+
}
|
|
255
742
|
selectionRectsForPage(pageNumber) {
|
|
256
743
|
return this.selectionRects().filter((rect) => rect.pageNumber === pageNumber);
|
|
257
744
|
}
|
|
258
|
-
isPageImageFresh(page) {
|
|
259
|
-
return !!page.url && Math.abs((page.renderedScale ?? 0) - page.scale) < 0.0001;
|
|
260
|
-
}
|
|
261
745
|
startTextSelection(event, page) {
|
|
262
746
|
if (event.button !== 0 || page.textGlyphs.length === 0) {
|
|
263
747
|
return;
|
|
@@ -295,17 +779,148 @@ class PdfViewer {
|
|
|
295
779
|
surface.releasePointerCapture?.(event.pointerId);
|
|
296
780
|
}
|
|
297
781
|
}
|
|
782
|
+
loadAnnotationsDataSource(dataSource, context, token) {
|
|
783
|
+
if (!dataSource) {
|
|
784
|
+
this.setAnnotationItems([], token);
|
|
785
|
+
return () => { };
|
|
786
|
+
}
|
|
787
|
+
try {
|
|
788
|
+
if (Array.isArray(dataSource)) {
|
|
789
|
+
this.setAnnotationItems(dataSource, token);
|
|
790
|
+
return () => { };
|
|
791
|
+
}
|
|
792
|
+
if (this.isServerAnnotationDataSource(dataSource)) {
|
|
793
|
+
let isActive = true;
|
|
794
|
+
dataSource.getAnnotations({
|
|
795
|
+
...context,
|
|
796
|
+
successCallback: (annotations) => {
|
|
797
|
+
if (isActive) {
|
|
798
|
+
this.setAnnotationItems(annotations, token);
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
failCallback: () => {
|
|
802
|
+
if (isActive) {
|
|
803
|
+
this.setAnnotationItems([], token);
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
});
|
|
807
|
+
return () => {
|
|
808
|
+
isActive = false;
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
const result = typeof dataSource === 'function' ? dataSource(context) : dataSource;
|
|
812
|
+
return this.applyAnnotationDataSourceResult(result, token);
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
this.setAnnotationItems([], token);
|
|
816
|
+
return () => { };
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
applyAnnotationDataSourceResult(result, token) {
|
|
820
|
+
if (isObservable(result)) {
|
|
821
|
+
const subscription = result.subscribe({
|
|
822
|
+
next: (annotations) => this.setAnnotationItems(annotations, token),
|
|
823
|
+
error: () => this.setAnnotationItems([], token),
|
|
824
|
+
});
|
|
825
|
+
return () => subscription.unsubscribe();
|
|
826
|
+
}
|
|
827
|
+
if (this.isPromiseLike(result)) {
|
|
828
|
+
let isActive = true;
|
|
829
|
+
result
|
|
830
|
+
.then((annotations) => {
|
|
831
|
+
if (isActive) {
|
|
832
|
+
this.setAnnotationItems(annotations, token);
|
|
833
|
+
}
|
|
834
|
+
})
|
|
835
|
+
.catch(() => {
|
|
836
|
+
if (isActive) {
|
|
837
|
+
this.setAnnotationItems([], token);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
return () => {
|
|
841
|
+
isActive = false;
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
this.setAnnotationItems(result, token);
|
|
845
|
+
return () => { };
|
|
846
|
+
}
|
|
847
|
+
setAnnotationItems(annotations, token) {
|
|
848
|
+
if (token === this.annotationDataSourceToken) {
|
|
849
|
+
this.annotationItems.set(annotations ?? []);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
isServerAnnotationDataSource(dataSource) {
|
|
853
|
+
return typeof dataSource === 'object'
|
|
854
|
+
&& dataSource !== null
|
|
855
|
+
&& 'getAnnotations' in dataSource
|
|
856
|
+
&& typeof dataSource.getAnnotations === 'function';
|
|
857
|
+
}
|
|
858
|
+
isPromiseLike(value) {
|
|
859
|
+
return typeof value === 'object'
|
|
860
|
+
&& value !== null
|
|
861
|
+
&& 'then' in value
|
|
862
|
+
&& typeof value.then === 'function';
|
|
863
|
+
}
|
|
298
864
|
cancelTextSelection() {
|
|
299
865
|
this.selectionStart = null;
|
|
300
866
|
}
|
|
867
|
+
async searchPdf(query, options) {
|
|
868
|
+
const token = ++this.searchToken;
|
|
869
|
+
const keyword = query.trim();
|
|
870
|
+
if (!keyword || !this.engine || !this.pdfDocument) {
|
|
871
|
+
this.pdfSearchResults.set([]);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const flags = [];
|
|
875
|
+
if (options.caseSensitive) {
|
|
876
|
+
flags.push(MatchFlag.MatchCase);
|
|
877
|
+
}
|
|
878
|
+
if (options.wholeWord) {
|
|
879
|
+
flags.push(MatchFlag.MatchWholeWord);
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
const searchResult = await this.engine.searchAllPages(this.pdfDocument, keyword, { flags }).toPromise();
|
|
883
|
+
if (token !== this.searchToken) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
this.pdfSearchResults.set(searchResult.results.map((result, index) => this.toSearchResultView(result, index)));
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
if (token === this.searchToken) {
|
|
890
|
+
this.pdfSearchResults.set([]);
|
|
891
|
+
this.error.emit(error);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
toSearchResultView(result, index) {
|
|
896
|
+
const context = result.context;
|
|
897
|
+
const excerpt = [
|
|
898
|
+
context.truncatedLeft ? '...' : '',
|
|
899
|
+
context.before,
|
|
900
|
+
context.match,
|
|
901
|
+
context.after,
|
|
902
|
+
context.truncatedRight ? '...' : '',
|
|
903
|
+
]
|
|
904
|
+
.filter((part) => part.length > 0)
|
|
905
|
+
.join(' ')
|
|
906
|
+
.replace(/\s+/g, ' ')
|
|
907
|
+
.trim();
|
|
908
|
+
return {
|
|
909
|
+
id: `${result.pageIndex}-${result.charIndex}-${result.charCount}-${index}`,
|
|
910
|
+
pageNumber: result.pageIndex + 1,
|
|
911
|
+
excerpt,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
301
914
|
async loadDocument(source, wasmUrl) {
|
|
302
915
|
const token = ++this.loadToken;
|
|
303
916
|
this.renderToken++;
|
|
304
917
|
this.isLoading.set(true);
|
|
305
918
|
this.errorState.set(null);
|
|
306
919
|
this.pageCount.set(0);
|
|
920
|
+
this.searchToken++;
|
|
307
921
|
this.selectionStart = null;
|
|
308
922
|
this.selectionRects.set([]);
|
|
923
|
+
this.pdfSearchResults.set([]);
|
|
309
924
|
this.revokeRenderedPages();
|
|
310
925
|
this.revokeThumbnailPages();
|
|
311
926
|
await this.closeDocument();
|
|
@@ -337,6 +952,10 @@ class PdfViewer {
|
|
|
337
952
|
this.loaded.emit({ pageCount: pdfDocument.pageCount });
|
|
338
953
|
this.initializePageShells(pdfDocument, this.zoom(), this.renderAll(), this.activePage());
|
|
339
954
|
this.schedulePageObserverRefresh();
|
|
955
|
+
await this.searchPdf(this.activeSearchQuery() || this.searchQuery(), {
|
|
956
|
+
caseSensitive: false,
|
|
957
|
+
wholeWord: false,
|
|
958
|
+
});
|
|
340
959
|
await this.renderVisiblePages(token, ++this.renderToken, {
|
|
341
960
|
zoom: this.zoom(),
|
|
342
961
|
activePage: this.activePage(),
|
|
@@ -366,12 +985,17 @@ class PdfViewer {
|
|
|
366
985
|
const pdfPage = pdfDocument.pages[pageNumber - 1];
|
|
367
986
|
const previousPage = previousPageMap.get(pageNumber);
|
|
368
987
|
const displaySize = this.getPageDisplaySize(pdfPage, nextScale);
|
|
369
|
-
const
|
|
988
|
+
const displayRotation = this.getPageDisplayRotation(pdfPage);
|
|
989
|
+
const isFresh = previousPage?.url
|
|
990
|
+
&& Math.abs((previousPage.renderedScale ?? 0) - nextScale) < 0.0001
|
|
991
|
+
&& previousPage.renderedRotation === displayRotation;
|
|
370
992
|
return {
|
|
371
993
|
pageNumber,
|
|
372
994
|
url: isFresh ? previousPage.url : null,
|
|
373
995
|
scale: nextScale,
|
|
374
996
|
renderedScale: isFresh ? previousPage.renderedScale : null,
|
|
997
|
+
rotation: displayRotation,
|
|
998
|
+
renderedRotation: isFresh ? previousPage.renderedRotation : null,
|
|
375
999
|
width: displaySize.width,
|
|
376
1000
|
height: displaySize.height,
|
|
377
1001
|
isRendering: false,
|
|
@@ -403,6 +1027,21 @@ class PdfViewer {
|
|
|
403
1027
|
this.cancelVisiblePageRenderFrame();
|
|
404
1028
|
const token = this.loadToken;
|
|
405
1029
|
const renderToken = ++this.renderToken;
|
|
1030
|
+
const renderDelay = this.getQualityRenderDelay();
|
|
1031
|
+
if (renderDelay > 0) {
|
|
1032
|
+
this.visiblePageRenderTimeout = targetWindow.setTimeout(() => {
|
|
1033
|
+
this.visiblePageRenderTimeout = null;
|
|
1034
|
+
this.queueVisiblePageRenderFrame(token, renderToken, options);
|
|
1035
|
+
}, renderDelay);
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
this.queueVisiblePageRenderFrame(token, renderToken, options);
|
|
1039
|
+
}
|
|
1040
|
+
queueVisiblePageRenderFrame(token, renderToken, options) {
|
|
1041
|
+
const targetWindow = this.document.defaultView;
|
|
1042
|
+
if (!targetWindow) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
406
1045
|
this.visiblePageRenderFrame = targetWindow.requestAnimationFrame(() => {
|
|
407
1046
|
this.visiblePageRenderFrame = null;
|
|
408
1047
|
void this.renderVisiblePages(token, renderToken, options);
|
|
@@ -429,17 +1068,22 @@ class PdfViewer {
|
|
|
429
1068
|
return;
|
|
430
1069
|
}
|
|
431
1070
|
const currentPage = this.renderedPages().find((page) => page.pageNumber === pageNumber);
|
|
1071
|
+
const page = pdfDocument.pages[pageNumber - 1];
|
|
1072
|
+
const renderRotation = this.getPageDisplayRotation(page);
|
|
432
1073
|
if (!currentPage || Math.abs(currentPage.scale - renderScale) > 0.0001) {
|
|
433
1074
|
continue;
|
|
434
1075
|
}
|
|
435
|
-
if (currentPage.url
|
|
1076
|
+
if (currentPage.url
|
|
1077
|
+
&& Math.abs((currentPage.renderedScale ?? 0) - renderScale) < 0.0001
|
|
1078
|
+
&& currentPage.renderedRotation === renderRotation) {
|
|
436
1079
|
continue;
|
|
437
1080
|
}
|
|
438
|
-
this.patchRenderedPage(pageNumber, { isRendering:
|
|
439
|
-
const
|
|
1081
|
+
this.patchRenderedPage(pageNumber, { isRendering: !currentPage.url });
|
|
1082
|
+
const rasterOptions = this.getPageRasterRenderOptions(page, renderScale);
|
|
440
1083
|
const blob = await this.engine.renderPage(pdfDocument, page, {
|
|
441
|
-
scaleFactor:
|
|
442
|
-
|
|
1084
|
+
scaleFactor: rasterOptions.scaleFactor,
|
|
1085
|
+
rotation: renderRotation,
|
|
1086
|
+
dpr: rasterOptions.dpr,
|
|
443
1087
|
withAnnotations: options.withAnnotations,
|
|
444
1088
|
withForms: options.withForms,
|
|
445
1089
|
}).toPromise();
|
|
@@ -447,7 +1091,9 @@ class PdfViewer {
|
|
|
447
1091
|
return;
|
|
448
1092
|
}
|
|
449
1093
|
const displaySize = this.getPageDisplaySize(page, renderScale);
|
|
450
|
-
const textGlyphs =
|
|
1094
|
+
const textGlyphs = this.pageRotation() === Rotation.Degree0
|
|
1095
|
+
? await this.getPageTextGlyphs(pdfDocument, page, renderScale)
|
|
1096
|
+
: [];
|
|
451
1097
|
if (!this.isRenderCurrent(token, renderToken)) {
|
|
452
1098
|
return;
|
|
453
1099
|
}
|
|
@@ -457,6 +1103,8 @@ class PdfViewer {
|
|
|
457
1103
|
url,
|
|
458
1104
|
scale: renderScale,
|
|
459
1105
|
renderedScale: renderScale,
|
|
1106
|
+
rotation: renderRotation,
|
|
1107
|
+
renderedRotation: renderRotation,
|
|
460
1108
|
width: displaySize.width,
|
|
461
1109
|
height: displaySize.height,
|
|
462
1110
|
isRendering: false,
|
|
@@ -471,6 +1119,7 @@ class PdfViewer {
|
|
|
471
1119
|
setZoom(scale) {
|
|
472
1120
|
const nextZoom = this.sanitizeScale(scale);
|
|
473
1121
|
if (this.zoom() !== nextZoom) {
|
|
1122
|
+
this.lastZoomChangeTime = this.getCurrentTime();
|
|
474
1123
|
this.zoom.set(nextZoom);
|
|
475
1124
|
}
|
|
476
1125
|
this.applyInstantZoom(nextZoom);
|
|
@@ -483,18 +1132,27 @@ class PdfViewer {
|
|
|
483
1132
|
return;
|
|
484
1133
|
}
|
|
485
1134
|
let hasChanges = false;
|
|
1135
|
+
const urlsToRevoke = [];
|
|
486
1136
|
const nextPages = pages.map((page) => {
|
|
487
|
-
|
|
1137
|
+
const pdfPage = pdfDocument.pages[page.pageNumber - 1];
|
|
1138
|
+
const displaySize = this.getPageDisplaySize(pdfPage, nextScale);
|
|
1139
|
+
const displayRotation = this.getPageDisplayRotation(pdfPage);
|
|
1140
|
+
const scaleChanged = Math.abs(page.scale - nextScale) >= 0.0001;
|
|
1141
|
+
const rotationChanged = page.rotation !== displayRotation || page.renderedRotation !== displayRotation;
|
|
1142
|
+
if (!scaleChanged && !rotationChanged && page.width === displaySize.width && page.height === displaySize.height) {
|
|
488
1143
|
return page;
|
|
489
1144
|
}
|
|
490
1145
|
hasChanges = true;
|
|
491
|
-
|
|
492
|
-
|
|
1146
|
+
if (rotationChanged && page.url) {
|
|
1147
|
+
urlsToRevoke.push(page.url);
|
|
1148
|
+
}
|
|
493
1149
|
return {
|
|
494
1150
|
...page,
|
|
495
|
-
url: null,
|
|
1151
|
+
url: rotationChanged ? null : page.url,
|
|
496
1152
|
scale: nextScale,
|
|
497
|
-
renderedScale: null,
|
|
1153
|
+
renderedScale: rotationChanged ? null : page.renderedScale,
|
|
1154
|
+
rotation: displayRotation,
|
|
1155
|
+
renderedRotation: rotationChanged ? null : page.renderedRotation,
|
|
498
1156
|
width: displaySize.width,
|
|
499
1157
|
height: displaySize.height,
|
|
500
1158
|
isRendering: false,
|
|
@@ -504,14 +1162,12 @@ class PdfViewer {
|
|
|
504
1162
|
if (!hasChanges) {
|
|
505
1163
|
return;
|
|
506
1164
|
}
|
|
507
|
-
for (const page of pages) {
|
|
508
|
-
if (page.url) {
|
|
509
|
-
this.revokeObjectUrl(page.url);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
1165
|
this.selectionStart = null;
|
|
513
1166
|
this.selectionRects.set([]);
|
|
514
1167
|
this.renderedPages.set(nextPages);
|
|
1168
|
+
for (const url of urlsToRevoke) {
|
|
1169
|
+
this.revokeObjectUrl(url);
|
|
1170
|
+
}
|
|
515
1171
|
this.schedulePageObserverRefresh();
|
|
516
1172
|
}
|
|
517
1173
|
getPriorityPageNumbers(activePage, renderAll) {
|
|
@@ -542,15 +1198,18 @@ class PdfViewer {
|
|
|
542
1198
|
return [];
|
|
543
1199
|
}
|
|
544
1200
|
const containerRect = container.getBoundingClientRect();
|
|
545
|
-
const
|
|
1201
|
+
const verticalPrefetchMargin = containerRect.height;
|
|
1202
|
+
const horizontalPrefetchMargin = containerRect.width;
|
|
546
1203
|
const pages = Array.from(container.querySelectorAll('[data-ngs-pdf-page]'));
|
|
547
1204
|
const visiblePageNumbers = [];
|
|
548
1205
|
for (const page of pages) {
|
|
549
1206
|
const rect = page.getBoundingClientRect();
|
|
550
1207
|
const pageNumber = Number(page.dataset['ngsPdfPage']);
|
|
551
1208
|
if (Number.isFinite(pageNumber) &&
|
|
552
|
-
rect.bottom >= containerRect.top -
|
|
553
|
-
rect.top <= containerRect.bottom +
|
|
1209
|
+
rect.bottom >= containerRect.top - verticalPrefetchMargin &&
|
|
1210
|
+
rect.top <= containerRect.bottom + verticalPrefetchMargin &&
|
|
1211
|
+
rect.right >= containerRect.left - horizontalPrefetchMargin &&
|
|
1212
|
+
rect.left <= containerRect.right + horizontalPrefetchMargin) {
|
|
554
1213
|
visiblePageNumbers.push(pageNumber);
|
|
555
1214
|
}
|
|
556
1215
|
}
|
|
@@ -570,6 +1229,7 @@ class PdfViewer {
|
|
|
570
1229
|
for (const page of pdfDocument.pages) {
|
|
571
1230
|
const blob = await this.engine.renderThumbnail(pdfDocument, page, {
|
|
572
1231
|
scaleFactor: thumbnailScale,
|
|
1232
|
+
rotation: this.getPageDisplayRotation(page),
|
|
573
1233
|
dpr: this.getDevicePixelRatio(),
|
|
574
1234
|
withAnnotations: options.withAnnotations,
|
|
575
1235
|
}).toPromise();
|
|
@@ -756,16 +1416,39 @@ class PdfViewer {
|
|
|
756
1416
|
}
|
|
757
1417
|
async openSource(engine, documentId, source) {
|
|
758
1418
|
if (typeof source === 'string') {
|
|
759
|
-
|
|
1419
|
+
const content = await this.fetchPdfSource(source);
|
|
1420
|
+
return this.openDocumentBuffer(engine, documentId, content);
|
|
760
1421
|
}
|
|
761
1422
|
if (source instanceof Blob) {
|
|
762
|
-
return
|
|
1423
|
+
return this.openDocumentBuffer(engine, documentId, await source.arrayBuffer());
|
|
763
1424
|
}
|
|
764
1425
|
if (source instanceof Uint8Array) {
|
|
765
1426
|
const content = new Uint8Array(source).buffer;
|
|
766
|
-
return
|
|
1427
|
+
return this.openDocumentBuffer(engine, documentId, content);
|
|
767
1428
|
}
|
|
768
|
-
return
|
|
1429
|
+
return this.openDocumentBuffer(engine, documentId, source);
|
|
1430
|
+
}
|
|
1431
|
+
async fetchPdfSource(source) {
|
|
1432
|
+
const response = await fetch(source);
|
|
1433
|
+
if (!response.ok) {
|
|
1434
|
+
throw new Error(`PDF request failed with status ${response.status}`);
|
|
1435
|
+
}
|
|
1436
|
+
return response.arrayBuffer();
|
|
1437
|
+
}
|
|
1438
|
+
openDocumentBuffer(engine, documentId, content) {
|
|
1439
|
+
return this.withTimeout(engine.openDocumentBuffer({ id: documentId, content }).toPromise(), this.documentOpenTimeoutMs, 'PDF document opening timed out');
|
|
1440
|
+
}
|
|
1441
|
+
withTimeout(promise, timeoutMs, message) {
|
|
1442
|
+
return new Promise((resolve, reject) => {
|
|
1443
|
+
const timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
1444
|
+
promise.then((value) => {
|
|
1445
|
+
clearTimeout(timeoutId);
|
|
1446
|
+
resolve(value);
|
|
1447
|
+
}, (error) => {
|
|
1448
|
+
clearTimeout(timeoutId);
|
|
1449
|
+
reject(error);
|
|
1450
|
+
});
|
|
1451
|
+
});
|
|
769
1452
|
}
|
|
770
1453
|
async closeDocument() {
|
|
771
1454
|
if (!this.engine || !this.pdfDocument) {
|
|
@@ -805,6 +1488,23 @@ class PdfViewer {
|
|
|
805
1488
|
}
|
|
806
1489
|
URL.revokeObjectURL(url);
|
|
807
1490
|
}
|
|
1491
|
+
getSourceName(source) {
|
|
1492
|
+
if (typeof File !== 'undefined' && source instanceof File && source.name) {
|
|
1493
|
+
return source.name;
|
|
1494
|
+
}
|
|
1495
|
+
if (typeof source !== 'string' || source.trim().length === 0) {
|
|
1496
|
+
return 'Document.pdf';
|
|
1497
|
+
}
|
|
1498
|
+
try {
|
|
1499
|
+
const url = new URL(source, this.document.baseURI);
|
|
1500
|
+
const pathName = url.pathname.split('/').filter(Boolean).pop();
|
|
1501
|
+
return pathName ? decodeURIComponent(pathName) : 'Document.pdf';
|
|
1502
|
+
}
|
|
1503
|
+
catch {
|
|
1504
|
+
const pathName = source.split('?')[0]?.split('#')[0]?.split('/').filter(Boolean).pop();
|
|
1505
|
+
return pathName ? decodeURIComponent(pathName) : 'Document.pdf';
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
808
1508
|
sanitizePage(pageNumber) {
|
|
809
1509
|
return this.clamp(Math.trunc(Number.isFinite(pageNumber) ? pageNumber : 1), 1, Math.max(this.pageCount(), 1));
|
|
810
1510
|
}
|
|
@@ -815,15 +1515,43 @@ class PdfViewer {
|
|
|
815
1515
|
roundZoom(value) {
|
|
816
1516
|
return this.sanitizeScale(Math.round(this.sanitizeScale(value) * 100) / 100);
|
|
817
1517
|
}
|
|
1518
|
+
floorFitZoom(value) {
|
|
1519
|
+
return this.sanitizeScale(Math.floor(this.sanitizeScale(value) * 100) / 100);
|
|
1520
|
+
}
|
|
818
1521
|
clamp(value, min, max) {
|
|
819
1522
|
return Math.min(Math.max(value, min), max);
|
|
820
1523
|
}
|
|
821
1524
|
roundCssPixel(value) {
|
|
822
1525
|
return Math.round(value * 100) / 100;
|
|
823
1526
|
}
|
|
1527
|
+
parseCssPixel(value) {
|
|
1528
|
+
const parsed = Number.parseFloat(value);
|
|
1529
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
1530
|
+
}
|
|
824
1531
|
getDevicePixelRatio() {
|
|
825
1532
|
return this.document.defaultView?.devicePixelRatio || 1;
|
|
826
1533
|
}
|
|
1534
|
+
getPageRasterRenderOptions(page, scale) {
|
|
1535
|
+
const devicePixelRatio = Math.max(1, this.getDevicePixelRatio());
|
|
1536
|
+
const pageSize = this.getPageBaseSize(page);
|
|
1537
|
+
const maxRenderPixels = this.sanitizePositiveNumber(this.maxRenderPixels(), 128_000_000);
|
|
1538
|
+
const maxRenderDimension = this.sanitizePositiveNumber(this.maxRenderDimension(), 13_000);
|
|
1539
|
+
const targetEffectiveScale = scale * devicePixelRatio;
|
|
1540
|
+
const dimensionEffectiveScale = maxRenderDimension / Math.max(pageSize.width, pageSize.height);
|
|
1541
|
+
const pixelEffectiveScale = Math.sqrt(maxRenderPixels / (pageSize.width * pageSize.height));
|
|
1542
|
+
const effectiveScale = this.clamp(Math.min(targetEffectiveScale, dimensionEffectiveScale, pixelEffectiveScale), 0.05, targetEffectiveScale);
|
|
1543
|
+
const dprAtLayoutScale = effectiveScale / scale;
|
|
1544
|
+
if (dprAtLayoutScale >= 1) {
|
|
1545
|
+
return {
|
|
1546
|
+
scaleFactor: scale,
|
|
1547
|
+
dpr: this.clamp(dprAtLayoutScale, 1, devicePixelRatio),
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
return {
|
|
1551
|
+
scaleFactor: effectiveScale,
|
|
1552
|
+
dpr: 1,
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
827
1555
|
createDocumentId() {
|
|
828
1556
|
return `ngs-pdf-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
829
1557
|
}
|
|
@@ -849,12 +1577,82 @@ class PdfViewer {
|
|
|
849
1577
|
this.pageObserverFrame = null;
|
|
850
1578
|
}
|
|
851
1579
|
cancelVisiblePageRenderFrame() {
|
|
1580
|
+
if (this.visiblePageRenderTimeout !== null) {
|
|
1581
|
+
this.document.defaultView?.clearTimeout(this.visiblePageRenderTimeout);
|
|
1582
|
+
this.visiblePageRenderTimeout = null;
|
|
1583
|
+
}
|
|
852
1584
|
if (this.visiblePageRenderFrame === null) {
|
|
853
1585
|
return;
|
|
854
1586
|
}
|
|
855
1587
|
this.document.defaultView?.cancelAnimationFrame(this.visiblePageRenderFrame);
|
|
856
1588
|
this.visiblePageRenderFrame = null;
|
|
857
1589
|
}
|
|
1590
|
+
getCurrentTime() {
|
|
1591
|
+
return this.document.defaultView?.performance?.now() ?? Date.now();
|
|
1592
|
+
}
|
|
1593
|
+
getQualityRenderDelay() {
|
|
1594
|
+
const elapsed = this.getCurrentTime() - this.lastZoomChangeTime;
|
|
1595
|
+
if (elapsed >= this.qualityRenderZoomIdleDelay) {
|
|
1596
|
+
return 0;
|
|
1597
|
+
}
|
|
1598
|
+
return Math.max(0, this.qualityRenderZoomIdleDelay - elapsed);
|
|
1599
|
+
}
|
|
1600
|
+
getZoomAnchor(container, event) {
|
|
1601
|
+
if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) {
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
const targetDocument = container.ownerDocument;
|
|
1605
|
+
const targetElement = targetDocument.elementFromPoint(event.clientX, event.clientY);
|
|
1606
|
+
const pageElement = targetElement?.closest('[data-ngs-pdf-page]');
|
|
1607
|
+
if (!pageElement || !container.contains(pageElement)) {
|
|
1608
|
+
return null;
|
|
1609
|
+
}
|
|
1610
|
+
const containerRect = container.getBoundingClientRect();
|
|
1611
|
+
const pageRect = pageElement.getBoundingClientRect();
|
|
1612
|
+
const pageNumber = Number(pageElement.dataset['ngsPdfPage']);
|
|
1613
|
+
if (!Number.isFinite(pageNumber) || pageRect.width <= 0 || pageRect.height <= 0) {
|
|
1614
|
+
return null;
|
|
1615
|
+
}
|
|
1616
|
+
return {
|
|
1617
|
+
container,
|
|
1618
|
+
pageNumber,
|
|
1619
|
+
relativeX: this.clamp((event.clientX - pageRect.left) / pageRect.width, 0, 1),
|
|
1620
|
+
relativeY: this.clamp((event.clientY - pageRect.top) / pageRect.height, 0, 1),
|
|
1621
|
+
viewportX: event.clientX - containerRect.left,
|
|
1622
|
+
viewportY: event.clientY - containerRect.top,
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
restoreZoomAnchor(anchor) {
|
|
1626
|
+
if (!anchor) {
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
const targetWindow = this.document.defaultView;
|
|
1630
|
+
const restore = () => {
|
|
1631
|
+
const pageElement = anchor.container.querySelector(`[data-ngs-pdf-page="${anchor.pageNumber}"]`);
|
|
1632
|
+
if (!pageElement) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const containerRect = anchor.container.getBoundingClientRect();
|
|
1636
|
+
const pageRect = pageElement.getBoundingClientRect();
|
|
1637
|
+
const targetLeft = anchor.container.scrollLeft +
|
|
1638
|
+
pageRect.left -
|
|
1639
|
+
containerRect.left +
|
|
1640
|
+
pageRect.width * anchor.relativeX -
|
|
1641
|
+
anchor.viewportX;
|
|
1642
|
+
const targetTop = anchor.container.scrollTop +
|
|
1643
|
+
pageRect.top -
|
|
1644
|
+
containerRect.top +
|
|
1645
|
+
pageRect.height * anchor.relativeY -
|
|
1646
|
+
anchor.viewportY;
|
|
1647
|
+
anchor.container.scrollLeft = this.clamp(targetLeft, 0, Math.max(0, anchor.container.scrollWidth - anchor.container.clientWidth));
|
|
1648
|
+
anchor.container.scrollTop = this.clamp(targetTop, 0, Math.max(0, anchor.container.scrollHeight - anchor.container.clientHeight));
|
|
1649
|
+
};
|
|
1650
|
+
if (!targetWindow) {
|
|
1651
|
+
restore();
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
targetWindow.requestAnimationFrame(restore);
|
|
1655
|
+
}
|
|
858
1656
|
disconnectPageObserver() {
|
|
859
1657
|
this.cancelPageObserverFrame();
|
|
860
1658
|
this.pageIntersectionObserver?.disconnect();
|
|
@@ -924,6 +1722,32 @@ class PdfViewer {
|
|
|
924
1722
|
}
|
|
925
1723
|
return true;
|
|
926
1724
|
}
|
|
1725
|
+
getFitScale(mode) {
|
|
1726
|
+
const container = this.viewerBody()?.nativeElement;
|
|
1727
|
+
const page = this.pdfDocument?.pages[this.clamp(this.activePage(), 1, Math.max(this.pageCount(), 1)) - 1];
|
|
1728
|
+
if (!container || !page) {
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
const pageSize = this.getPageBaseSize(page);
|
|
1732
|
+
const pagesElement = container.querySelector('.pdf-viewer-pages');
|
|
1733
|
+
const computedStyle = pagesElement && this.document.defaultView
|
|
1734
|
+
? this.document.defaultView.getComputedStyle(pagesElement)
|
|
1735
|
+
: null;
|
|
1736
|
+
const horizontalPadding = computedStyle
|
|
1737
|
+
? this.parseCssPixel(computedStyle.paddingLeft) + this.parseCssPixel(computedStyle.paddingRight)
|
|
1738
|
+
: 0;
|
|
1739
|
+
const verticalPadding = computedStyle
|
|
1740
|
+
? this.parseCssPixel(computedStyle.paddingTop) + this.parseCssPixel(computedStyle.paddingBottom)
|
|
1741
|
+
: 0;
|
|
1742
|
+
const fitAllowance = 1;
|
|
1743
|
+
const availableWidth = Math.max(1, container.clientWidth - horizontalPadding - fitAllowance);
|
|
1744
|
+
const availableHeight = Math.max(1, container.clientHeight - verticalPadding - fitAllowance);
|
|
1745
|
+
const widthScale = availableWidth / pageSize.width;
|
|
1746
|
+
if (mode === 'fit-width') {
|
|
1747
|
+
return this.floorFitZoom(widthScale);
|
|
1748
|
+
}
|
|
1749
|
+
return this.floorFitZoom(Math.min(widthScale, availableHeight / pageSize.height));
|
|
1750
|
+
}
|
|
927
1751
|
getScaleBounds() {
|
|
928
1752
|
const min = this.sanitizePositiveNumber(this.minScale(), 0.2);
|
|
929
1753
|
const max = this.sanitizePositiveNumber(this.maxScale(), 60);
|
|
@@ -936,14 +1760,28 @@ class PdfViewer {
|
|
|
936
1760
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
937
1761
|
}
|
|
938
1762
|
getPageDisplaySize(page, scale) {
|
|
939
|
-
const
|
|
1763
|
+
const pageSize = this.getPageBaseSize(page);
|
|
1764
|
+
return {
|
|
1765
|
+
width: Math.max(1, Math.round(pageSize.width * scale)),
|
|
1766
|
+
height: Math.max(1, Math.round(pageSize.height * scale)),
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
getPageBaseSize(page) {
|
|
1770
|
+
const rotation = this.getPageDisplayRotation(page);
|
|
1771
|
+
const isRotatedSideways = rotation === Rotation.Degree90 || rotation === Rotation.Degree270;
|
|
940
1772
|
const width = isRotatedSideways ? page.size.height : page.size.width;
|
|
941
1773
|
const height = isRotatedSideways ? page.size.width : page.size.height;
|
|
942
1774
|
return {
|
|
943
|
-
width: Math.max(1,
|
|
944
|
-
height: Math.max(1,
|
|
1775
|
+
width: Math.max(1, width),
|
|
1776
|
+
height: Math.max(1, height),
|
|
945
1777
|
};
|
|
946
1778
|
}
|
|
1779
|
+
getPageDisplayRotation(page) {
|
|
1780
|
+
return this.normalizeRotation(page.rotation + this.pageRotation());
|
|
1781
|
+
}
|
|
1782
|
+
normalizeRotation(rotation) {
|
|
1783
|
+
return (((rotation % 4) + 4) % 4);
|
|
1784
|
+
}
|
|
947
1785
|
scrollToPage(pageNumber) {
|
|
948
1786
|
if (!this.renderAll()) {
|
|
949
1787
|
return;
|
|
@@ -954,10 +1792,12 @@ class PdfViewer {
|
|
|
954
1792
|
return;
|
|
955
1793
|
}
|
|
956
1794
|
const nextScrollTop = this.getPageScrollTop(container, target);
|
|
957
|
-
const
|
|
1795
|
+
const nextScrollLeft = this.getPageScrollLeft(container, target);
|
|
1796
|
+
const scrollDistance = Math.hypot(container.scrollTop - nextScrollTop, container.scrollLeft - nextScrollLeft);
|
|
958
1797
|
this.startProgrammaticScrollLock(pageNumber, scrollDistance);
|
|
959
1798
|
container.scrollTo({
|
|
960
1799
|
top: nextScrollTop,
|
|
1800
|
+
left: nextScrollLeft,
|
|
961
1801
|
behavior: 'smooth',
|
|
962
1802
|
});
|
|
963
1803
|
}
|
|
@@ -997,27 +1837,35 @@ class PdfViewer {
|
|
|
997
1837
|
return null;
|
|
998
1838
|
}
|
|
999
1839
|
const containerRect = container.getBoundingClientRect();
|
|
1000
|
-
const
|
|
1840
|
+
const viewportCenterX = containerRect.left + containerRect.width / 2;
|
|
1841
|
+
const viewportCenterY = containerRect.top + containerRect.height / 2;
|
|
1001
1842
|
const pages = Array.from(container.querySelectorAll('[data-ngs-pdf-page]'));
|
|
1002
1843
|
let closestPage = null;
|
|
1003
1844
|
let closestDistance = Number.POSITIVE_INFINITY;
|
|
1004
1845
|
let mostVisiblePage = null;
|
|
1005
|
-
let
|
|
1846
|
+
let mostVisibleArea = 0;
|
|
1006
1847
|
for (const page of pages) {
|
|
1007
1848
|
const rect = page.getBoundingClientRect();
|
|
1008
1849
|
const pageNumber = Number(page.dataset['ngsPdfPage']);
|
|
1009
1850
|
if (!Number.isFinite(pageNumber)) {
|
|
1010
1851
|
continue;
|
|
1011
1852
|
}
|
|
1012
|
-
if (rect.
|
|
1853
|
+
if (rect.left <= viewportCenterX &&
|
|
1854
|
+
rect.right >= viewportCenterX &&
|
|
1855
|
+
rect.top <= viewportCenterY &&
|
|
1856
|
+
rect.bottom >= viewportCenterY) {
|
|
1013
1857
|
return pageNumber;
|
|
1014
1858
|
}
|
|
1859
|
+
const visibleWidth = Math.max(0, Math.min(rect.right, containerRect.right) - Math.max(rect.left, containerRect.left));
|
|
1015
1860
|
const visibleHeight = Math.max(0, Math.min(rect.bottom, containerRect.bottom) - Math.max(rect.top, containerRect.top));
|
|
1016
|
-
|
|
1017
|
-
|
|
1861
|
+
const visibleArea = visibleWidth * visibleHeight;
|
|
1862
|
+
if (visibleArea > mostVisibleArea) {
|
|
1863
|
+
mostVisibleArea = visibleArea;
|
|
1018
1864
|
mostVisiblePage = pageNumber;
|
|
1019
1865
|
}
|
|
1020
|
-
const
|
|
1866
|
+
const pageCenterX = rect.left + rect.width / 2;
|
|
1867
|
+
const pageCenterY = rect.top + rect.height / 2;
|
|
1868
|
+
const distance = Math.hypot(pageCenterX - viewportCenterX, pageCenterY - viewportCenterY);
|
|
1021
1869
|
if (distance < closestDistance) {
|
|
1022
1870
|
closestDistance = distance;
|
|
1023
1871
|
closestPage = pageNumber;
|
|
@@ -1046,6 +1894,12 @@ class PdfViewer {
|
|
|
1046
1894
|
getPageScrollTop(container, target) {
|
|
1047
1895
|
return this.getElementScrollTop(container, target, 'start');
|
|
1048
1896
|
}
|
|
1897
|
+
getPageScrollLeft(container, target) {
|
|
1898
|
+
const containerRect = container.getBoundingClientRect();
|
|
1899
|
+
const targetRect = target.getBoundingClientRect();
|
|
1900
|
+
const targetLeft = container.scrollLeft + targetRect.left - containerRect.left;
|
|
1901
|
+
return this.clamp(targetLeft, 0, Math.max(0, container.scrollWidth - container.clientWidth));
|
|
1902
|
+
}
|
|
1049
1903
|
getElementScrollTop(container, target, align) {
|
|
1050
1904
|
const containerRect = container.getBoundingClientRect();
|
|
1051
1905
|
const targetRect = target.getBoundingClientRect();
|
|
@@ -1084,7 +1938,8 @@ class PdfViewer {
|
|
|
1084
1938
|
this.completeProgrammaticScroll();
|
|
1085
1939
|
return;
|
|
1086
1940
|
}
|
|
1087
|
-
if (Math.abs(container.scrollTop - this.getPageScrollTop(container, target)) <= 2
|
|
1941
|
+
if (Math.abs(container.scrollTop - this.getPageScrollTop(container, target)) <= 2 &&
|
|
1942
|
+
Math.abs(container.scrollLeft - this.getPageScrollLeft(container, target)) <= 2) {
|
|
1088
1943
|
this.completeProgrammaticScroll();
|
|
1089
1944
|
}
|
|
1090
1945
|
}
|
|
@@ -1108,13 +1963,14 @@ class PdfViewer {
|
|
|
1108
1963
|
this.programmaticScrollTargetPage = null;
|
|
1109
1964
|
}
|
|
1110
1965
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1111
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PdfViewer, isStandalone: true, selector: "ngs-pdf-viewer", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, wasmUrl: { classPropertyName: "wasmUrl", publicName: "wasmUrl", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, minScale: { classPropertyName: "minScale", publicName: "minScale", isSignal: true, isRequired: false, transformFunction: null }, maxScale: { classPropertyName: "maxScale", publicName: "maxScale", isSignal: true, isRequired: false, transformFunction: null }, zoomStep: { classPropertyName: "zoomStep", publicName: "zoomStep", isSignal: true, isRequired: false, transformFunction: null }, renderAll: { classPropertyName: "renderAll", publicName: "renderAll", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showPageList: { classPropertyName: "showPageList", publicName: "showPageList", isSignal: true, isRequired: false, transformFunction: null }, withAnnotations: { classPropertyName: "withAnnotations", publicName: "withAnnotations", isSignal: true, isRequired: false, transformFunction: null }, withForms: { classPropertyName: "withForms", publicName: "withForms", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", pageChanged: "pageChanged", pageRendered: "pageRendered", error: "error" }, host: { properties: { "class.is-loading": "isLoading()", "class.has-toolbar": "showToolbar()", "class.has-page-list": "isPageListVisible()", "class.has-error": "errorState()" }, classAttribute: "ngs-pdf-viewer not-prose" }, viewQueries: [{ propertyName: "viewerBody", first: true, predicate: ["viewerBody"], descendants: true, isSignal: true }, { propertyName: "pageList", first: true, predicate: ["pageList"], descendants: true, isSignal: true }], exportAs: ["ngsPdfViewer"], ngImport: i0, template: "<ngs-panel class=\"pdf-viewer-panel\">\n @if (showToolbar()) {\n <ngs-panel-header flex>\n <div class=\"pdf-viewer-toolbar\" aria-label=\"PDF viewer controls\">\n @if (showPageList()) {\n <button\n ngsIconButton\n type=\"button\"\n [attr.aria-label]=\"pageListVisible() ? 'Hide pages panel' : 'Show pages panel'\"\n [attr.aria-pressed]=\"pageListVisible()\"\n (click)=\"togglePageList()\">\n <ngs-icon\n [name]=\"pageListVisible() ? 'fluent:panel-left-contract-24-regular' : 'fluent:panel-left-expand-24-regular'\" />\n </button>\n }\n <button ngsIconButton type=\"button\" aria-label=\"Previous page\" [disabled]=\"!canGoPrevious()\" (click)=\"previousPage()\">\n <ngs-icon name=\"fluent:chevron-up-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__page\">{{ activePage() }} / {{ pageCount() || 1 }}</span>\n <button ngsIconButton type=\"button\" aria-label=\"Next page\" [disabled]=\"!canGoNext()\" (click)=\"nextPage()\">\n <ngs-icon name=\"fluent:chevron-down-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__spacer\"></span>\n <button ngsIconButton type=\"button\" aria-label=\"Zoom out\" [disabled]=\"!canZoomOut()\" (click)=\"zoomOut()\">\n <ngs-icon name=\"fluent:zoom-out-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__zoom\">{{ zoomLabel() }}</span>\n <button ngsIconButton type=\"button\" aria-label=\"Zoom in\" [disabled]=\"!canZoomIn()\" (click)=\"zoomIn()\">\n <ngs-icon name=\"fluent:zoom-in-24-regular\" />\n </button>\n <button ngsIconButton type=\"button\" aria-label=\"PDF viewer settings\" [ngsMenuTriggerFor]=\"settingsMenu\">\n <ngs-icon name=\"fluent:settings-24-regular\" />\n </button>\n </div>\n\n <ngs-menu #settingsMenu=\"ngsMenu\" xPosition=\"before\">\n <ngs-menu-heading>SPREAD MODE</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'single'\"\n (click)=\"setSpreadMode('single')\">\n <ngs-icon name=\"fluent:document-24-regular\" />\n <span>Single Page</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-odd'\"\n (click)=\"setSpreadMode('two-odd')\">\n <ngs-icon name=\"fluent:book-open-24-regular\" />\n <span>Two Page (Odd)</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-even'\"\n (click)=\"setSpreadMode('two-even')\">\n <ngs-icon name=\"fluent:document-text-24-regular\" />\n <span>Two Page (Even)</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>SCROLL LAYOUT</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'vertical'\"\n (click)=\"setScrollLayout('vertical')\">\n <ngs-icon name=\"fluent:arrow-sort-24-regular\" />\n <span>Vertical</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'horizontal'\"\n (click)=\"setScrollLayout('horizontal')\">\n <ngs-icon name=\"fluent:arrow-swap-24-regular\" />\n <span>Horizontal</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>PAGE ROTATION</ngs-menu-heading>\n <button ngs-menu-item type=\"button\" (click)=\"rotateClockwise()\">\n <ngs-icon name=\"fluent:arrow-clockwise-24-regular\" />\n <span>Rotate Clockwise</span>\n </button>\n <button ngs-menu-item type=\"button\" (click)=\"rotateCounterClockwise()\">\n <ngs-icon name=\"fluent:arrow-counterclockwise-24-regular\" />\n <span>Rotate Counter-Clockwise</span>\n </button>\n\n <ngs-menu-divider />\n <button ngs-menu-item type=\"button\" (click)=\"toggleFullscreen()\">\n <ngs-icon name=\"fluent:arrow-maximize-24-regular\" />\n <span>Fullscreen</span>\n </button>\n </ngs-menu>\n </ngs-panel-header>\n }\n\n @if (isPageListVisible()) {\n <ngs-panel-sidebar>\n <div class=\"pdf-viewer-sidebar\" #pageList>\n <div class=\"pdf-viewer-sidebar__title\">Pages</div>\n <div class=\"pdf-viewer-sidebar__pages\" aria-label=\"PDF pages\">\n @for (pageItem of pageItems(); track pageItem.pageNumber) {\n <button\n class=\"pdf-viewer-sidebar__page\"\n type=\"button\"\n [class.is-active]=\"pageItem.pageNumber === activePage()\"\n [attr.aria-current]=\"pageItem.pageNumber === activePage() ? 'page' : null\"\n [attr.aria-label]=\"'Open page ' + pageItem.pageNumber\"\n [attr.data-ngs-pdf-page-button]=\"pageItem.pageNumber\"\n (click)=\"setPage(pageItem.pageNumber)\">\n <span class=\"pdf-viewer-sidebar__thumb\" aria-hidden=\"true\">\n @if (pageItem.thumbnail; as thumbnail) {\n <img\n [src]=\"thumbnail.url\"\n [attr.width]=\"thumbnail.width\"\n [attr.height]=\"thumbnail.height\"\n alt=\"\"\n draggable=\"false\" />\n } @else {\n <ngs-image-placeholder />\n }\n </span>\n <span class=\"pdf-viewer-sidebar__label\">Page {{ pageItem.pageNumber }}</span>\n </button>\n }\n </div>\n </div>\n </ngs-panel-sidebar>\n }\n\n <ngs-panel-content>\n <div class=\"pdf-viewer-body\" #viewerBody (scroll)=\"onViewerScroll()\">\n <ngs-block-loader [loading]=\"isLoading()\">Loading PDF...</ngs-block-loader>\n @if (errorState()) {\n <div class=\"pdf-viewer-state pdf-viewer-state--error\">\n <ngs-icon name=\"fluent:error-circle-24-regular\" />\n <span>PDF could not be loaded.</span>\n </div>\n } @else if (!src()) {\n <div class=\"pdf-viewer-state\">\n <ngs-icon name=\"fluent:document-pdf-24-regular\" />\n <span>No PDF selected.</span>\n </div>\n } @else {\n <div class=\"pdf-viewer-pages\">\n @for (pdfPage of renderedPages(); track pdfPage.pageNumber) {\n <article\n class=\"pdf-viewer-page\"\n [attr.aria-label]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.data-ngs-pdf-page]=\"pdfPage.pageNumber\">\n <div\n class=\"pdf-viewer-page__surface\"\n [style.width.px]=\"pdfPage.width\"\n [style.height.px]=\"pdfPage.height\"\n (pointerdown)=\"startTextSelection($event, pdfPage)\"\n (pointermove)=\"updateTextSelection($event, pdfPage)\"\n (pointerup)=\"finishTextSelection($event, pdfPage)\"\n (pointercancel)=\"cancelTextSelection()\"\n (mousedown)=\"startTextSelection($event, pdfPage)\"\n (mousemove)=\"updateTextSelection($event, pdfPage)\"\n (mouseup)=\"finishTextSelection($event, pdfPage)\">\n @if (isPageImageFresh(pdfPage) && pdfPage.url; as pageUrl) {\n <img\n [src]=\"pageUrl\"\n [alt]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.width]=\"pdfPage.width\"\n [attr.height]=\"pdfPage.height\"\n draggable=\"false\" />\n } @else {\n <div class=\"pdf-viewer-page__placeholder\" aria-hidden=\"true\">\n @if (pdfPage.isRendering) {\n <span></span>\n }\n </div>\n }\n @if (selectionRectsForPage(pdfPage.pageNumber); as selectionRects) {\n @if (selectionRects.length > 0) {\n <div class=\"pdf-viewer-page__selection-layer\" aria-hidden=\"true\">\n @for (selectionRect of selectionRects; track selectionRect.left + '-' + selectionRect.top + '-' + $index) {\n <div\n class=\"pdf-viewer-page__selection-rect\"\n [style.left.px]=\"selectionRect.left\"\n [style.top.px]=\"selectionRect.top\"\n [style.width.px]=\"selectionRect.width\"\n [style.height.px]=\"selectionRect.height\"></div>\n }\n </div>\n }\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n </ngs-panel-content>\n</ngs-panel>\n", styles: [":host{--ngs-pdf-viewer-height: 640px;--ngs-pdf-viewer-background: var(--ngs-color-surface-container-low, #f5f6f8);--ngs-pdf-viewer-page-background: var(--ngs-color-surface, #ffffff);--ngs-pdf-viewer-page-shadow: var(--ngs-shadow-md, 0 8px 24px rgba(15, 23, 42, .12));--ngs-pdf-viewer-border-color: var(--ngs-color-outline-variant, rgba(15, 23, 42, .12));--ngs-pdf-viewer-radius: var(--ngs-radius-md, 10px);--ngs-pdf-viewer-sidebar-width: 152px;--ngs-pdf-viewer-selection-background: rgb(33 150 243 / .34);display:block;height:var(--ngs-pdf-viewer-height);min-height:320px;overflow:hidden;border:1px solid var(--ngs-pdf-viewer-border-color);border-radius:var(--ngs-pdf-viewer-radius);background:var(--ngs-pdf-viewer-background);color:var(--ngs-color-on-surface, #111827)}:host ngs-panel{height:100%;overflow:hidden;border-radius:inherit;background:var(--ngs-pdf-viewer-background)}:host ngs-panel-header{min-width:0;border-bottom:1px solid var(--ngs-pdf-viewer-border-color);background:var(--ngs-color-surface, #ffffff)}:host ngs-panel-sidebar{width:var(--ngs-pdf-viewer-sidebar-width);min-width:var(--ngs-pdf-viewer-sidebar-width);border-right:1px solid var(--ngs-pdf-viewer-border-color);background:var(--ngs-color-surface, #ffffff)}:host ngs-panel-content{min-width:0;min-height:0;overflow:hidden;background:var(--ngs-pdf-viewer-background)}:host .pdf-viewer-toolbar{display:flex;align-items:center;gap:var(--ngs-spacing-1, .25rem);width:100%;padding:0 var(--ngs-spacing-2, .5rem)}:host .pdf-viewer-toolbar__page,:host .pdf-viewer-toolbar__zoom{min-width:76px;text-align:center;font-size:var(--ngs-font-size-sm, .875rem);font-weight:500;color:var(--ngs-color-on-surface-variant, #4b5563)}:host .pdf-viewer-toolbar__spacer{flex:1 1 auto}:host .pdf-viewer-sidebar{display:flex;flex-direction:column;gap:var(--ngs-spacing-2, .5rem);height:100%;min-height:0;padding:var(--ngs-spacing-3, .75rem);overflow:auto}:host .pdf-viewer-sidebar__title{padding:0 var(--ngs-spacing-1, .25rem);font-size:var(--ngs-font-size-xs, .75rem);font-weight:600;color:var(--ngs-color-on-surface-variant, #6b7280)}:host .pdf-viewer-sidebar__pages{display:flex;flex-direction:column;gap:var(--ngs-spacing-2, .5rem);min-height:0}:host .pdf-viewer-sidebar__page{display:flex;flex-direction:column;align-items:stretch;gap:var(--ngs-spacing-1, .25rem);width:100%;padding:var(--ngs-spacing-1, .25rem);border:1px solid transparent;border-radius:calc(var(--ngs-pdf-viewer-radius) - 4px);background:transparent;color:var(--ngs-color-on-surface-variant, #4b5563);cursor:pointer;text-align:left}:host .pdf-viewer-sidebar__page:hover .pdf-viewer-sidebar__thumb{border-color:var(--ngs-color-outline, rgba(15, 23, 42, .24))}:host .pdf-viewer-sidebar__page:focus-visible{outline:2px solid var(--ngs-color-primary, #155eef);outline-offset:2px}:host .pdf-viewer-sidebar__page.is-active{color:var(--ngs-color-on-surface, #111827)}:host .pdf-viewer-sidebar__page.is-active .pdf-viewer-sidebar__thumb{border-color:var(--ngs-color-primary, #155eef);background:var(--ngs-color-primary-container, rgba(21, 94, 239, .08));box-shadow:0 0 0 2px var(--ngs-color-primary-container, rgba(21, 94, 239, .14)),var(--ngs-shadow-xs, 0 1px 2px rgba(15, 23, 42, .08))}:host .pdf-viewer-sidebar__thumb{display:flex;align-items:center;justify-content:center;aspect-ratio:3/4;width:100%;overflow:hidden;border:1px solid var(--ngs-pdf-viewer-border-color);border-radius:calc(var(--ngs-pdf-viewer-radius) - 6px);background:var(--ngs-pdf-viewer-page-background);box-shadow:var(--ngs-shadow-xs, 0 1px 2px rgba(15, 23, 42, .08))}:host .pdf-viewer-sidebar__thumb img{display:block;width:100%;height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}:host .pdf-viewer-sidebar__thumb ngs-image-placeholder{width:100%;height:100%}:host .pdf-viewer-sidebar__thumb ngs-image-placeholder ::ng-deep svg{width:64%;min-width:0}:host .pdf-viewer-sidebar__thumb ngs-icon{font-size:1.375rem;color:var(--ngs-color-on-surface-variant, #6b7280)}:host .pdf-viewer-sidebar__label{display:block;overflow:hidden;font-size:var(--ngs-font-size-xs, .75rem);font-weight:500;line-height:1.25;text-align:center;text-overflow:ellipsis;white-space:nowrap}:host .pdf-viewer-body{position:absolute;inset:0;overflow:auto}:host .pdf-viewer-pages{display:flex;flex-direction:column;align-items:center;gap:var(--ngs-spacing-6, 1.5rem);min-height:100%;padding:var(--ngs-spacing-6, 1.5rem)}:host .pdf-viewer-page{width:max-content;max-width:none}:host .pdf-viewer-page__surface{position:relative;overflow:hidden;cursor:text;-webkit-user-select:none;user-select:none;border-radius:calc(var(--ngs-pdf-viewer-radius) - 4px);background:var(--ngs-pdf-viewer-page-background);box-shadow:var(--ngs-pdf-viewer-page-shadow);touch-action:none}:host .pdf-viewer-page__surface img{display:block;width:100%;height:100%;max-width:none;-webkit-user-select:none;user-select:none;pointer-events:none}:host .pdf-viewer-page__placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--ngs-pdf-viewer-page-background);pointer-events:none}:host .pdf-viewer-page__placeholder span{width:32px;height:32px;border:2px solid var(--ngs-color-outline-variant, rgba(15, 23, 42, .12));border-top-color:var(--ngs-color-primary, #155eef);border-radius:999px;animation:ngs-pdf-viewer-page-spin .8s linear infinite}:host .pdf-viewer-page__selection-layer{position:absolute;inset:0;z-index:1;overflow:hidden;isolation:isolate;mix-blend-mode:multiply;forced-color-adjust:none;pointer-events:none}:host .pdf-viewer-page__selection-rect{position:absolute;background:var(--ngs-pdf-viewer-selection-background);pointer-events:none}:host .pdf-viewer-state{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ngs-spacing-3, .75rem);padding:var(--ngs-spacing-6, 1.5rem);color:var(--ngs-color-on-surface-variant, #6b7280);text-align:center}:host .pdf-viewer-state ngs-icon{font-size:2rem}:host .pdf-viewer-state--error{color:var(--ngs-color-danger, #dc2626)}@keyframes ngs-pdf-viewer-page-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: BlockLoader, selector: "ngs-block-loader", inputs: ["loading", "spinnerDiameter", "spinnerStrokeWidth"], exportAs: ["ngsBlockLoader"] }, { kind: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: ImagePlaceholder, selector: "ngs-image-placeholder", exportAs: ["ngsImagePlaceholder"] }, { kind: "component", type: Menu, selector: "ngs-menu", inputs: ["role", "classList", "xPosition", "yPosition"], outputs: ["closed"], exportAs: ["ngsMenu"] }, { kind: "component", type: MenuDivider, selector: "ngs-menu-divider" }, { kind: "component", type: MenuHeading, selector: "ngs-menu-heading" }, { kind: "component", type: MenuItem, selector: "ngs-menu-item, [ngs-menu-item]", inputs: ["disabled", "role", "selected"], outputs: ["_triggered"], exportAs: ["ngsMenuItem"] }, { kind: "directive", type: MenuTrigger, selector: "[ngsMenuTriggerFor]", inputs: ["ngsMenuTriggerFor", "ngsMenuTriggerData", "ngsMenuDisabled", "xPosition", "yPosition", "ngsMenuTriggerRestoreFocus"], outputs: ["menuOpened", "menuClosed"], exportAs: ["ngsMenuTrigger"] }, { kind: "component", type: Panel, selector: "ngs-panel", inputs: ["absolute"], exportAs: ["ngsPanel"] }, { kind: "component", type: PanelContent, selector: "ngs-panel-content", exportAs: ["ngsPanelContent"] }, { kind: "component", type: PanelHeader, selector: "ngs-panel-header", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelHeader"] }, { kind: "component", type: PanelSidebar, selector: "ngs-panel-sidebar" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1966
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.4", type: PdfViewer, isStandalone: true, selector: "ngs-pdf-viewer", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, documentName: { classPropertyName: "documentName", publicName: "documentName", isSignal: true, isRequired: false, transformFunction: null }, wasmUrl: { classPropertyName: "wasmUrl", publicName: "wasmUrl", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, scale: { classPropertyName: "scale", publicName: "scale", isSignal: true, isRequired: false, transformFunction: null }, minScale: { classPropertyName: "minScale", publicName: "minScale", isSignal: true, isRequired: false, transformFunction: null }, maxScale: { classPropertyName: "maxScale", publicName: "maxScale", isSignal: true, isRequired: false, transformFunction: null }, zoomStep: { classPropertyName: "zoomStep", publicName: "zoomStep", isSignal: true, isRequired: false, transformFunction: null }, maxRenderPixels: { classPropertyName: "maxRenderPixels", publicName: "maxRenderPixels", isSignal: true, isRequired: false, transformFunction: null }, maxRenderDimension: { classPropertyName: "maxRenderDimension", publicName: "maxRenderDimension", isSignal: true, isRequired: false, transformFunction: null }, renderAll: { classPropertyName: "renderAll", publicName: "renderAll", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showPageList: { classPropertyName: "showPageList", publicName: "showPageList", isSignal: true, isRequired: false, transformFunction: null }, showSearchPanel: { classPropertyName: "showSearchPanel", publicName: "showSearchPanel", isSignal: true, isRequired: false, transformFunction: null }, showAnnotationsPanel: { classPropertyName: "showAnnotationsPanel", publicName: "showAnnotationsPanel", isSignal: true, isRequired: false, transformFunction: null }, annotations: { classPropertyName: "annotations", publicName: "annotations", isSignal: true, isRequired: false, transformFunction: null }, annotationsDataSource: { classPropertyName: "annotationsDataSource", publicName: "annotationsDataSource", isSignal: true, isRequired: false, transformFunction: null }, annotationTypeProperty: { classPropertyName: "annotationTypeProperty", publicName: "annotationTypeProperty", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, withAnnotations: { classPropertyName: "withAnnotations", publicName: "withAnnotations", isSignal: true, isRequired: false, transformFunction: null }, withForms: { classPropertyName: "withForms", publicName: "withForms", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", pageChanged: "pageChanged", pageRendered: "pageRendered", error: "error" }, host: { properties: { "class.is-loading": "isLoading()", "class.has-toolbar": "showToolbar()", "class.has-page-list": "isPageListVisible()", "class.has-error": "errorState()" }, classAttribute: "ngs-pdf-viewer not-prose" }, queries: [{ propertyName: "annotationDefs", predicate: PdfViewerAnnotationDef, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "viewerBody", first: true, predicate: ["viewerBody"], descendants: true, isSignal: true }, { propertyName: "pageList", first: true, predicate: ["pageList"], descendants: true, isSignal: true }], exportAs: ["ngsPdfViewer"], ngImport: i0, template: "<ngs-panel class=\"pdf-viewer-panel h-full overflow-hidden rounded-lg border border-border bg-surface-container-low text-on-surface\">\n @if (showToolbar()) {\n <ngs-panel-header class=\"min-w-0 border-b border-border bg-surface\" flex>\n <ngs-toolbar class=\"pdf-viewer-toolbar flex h-14 w-full min-w-0 items-center gap-4 overflow-visible px-2 text-on-surface-variant\" aria-label=\"PDF viewer controls\">\n <ngs-toolbar-item class=\"pdf-viewer-toolbar__start flex min-w-0 flex-none items-center gap-2\">\n @if (showPageList()) {\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n [attr.aria-label]=\"pageListVisible() ? 'Hide pages panel' : 'Show pages panel'\"\n [attr.aria-pressed]=\"pageListVisible()\"\n (click)=\"togglePageList()\">\n <ngs-icon\n class=\"text-2xl\"\n [name]=\"pageListVisible() ? 'fluent:panel-left-contract-24-regular' : 'fluent:panel-left-expand-24-regular'\" />\n </button>\n }\n <ngs-divider vertical fixedHeight />\n </ngs-toolbar-item>\n <ngs-toolbar-title class=\"min-w-0 flex-1 truncate text-sm! max-w-[20%] font-semibold! text-on-surface-variant\" [title]=\"displayDocumentName()\">\n {{ displayDocumentName() }}\n </ngs-toolbar-title>\n\n <ngs-toolbar-item class=\"pdf-viewer-toolbar__center flex flex-none items-center gap-3\">\n <div class=\"pdf-viewer-toolbar__cluster flex min-w-0 items-center gap-2\" aria-label=\"Zoom controls\">\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Zoom out\"\n [disabled]=\"!canZoomOut()\"\n (click)=\"zoomOut()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:zoom-out-24-regular\" />\n </button>\n <button\n class=\"pdf-viewer-toolbar__zoom min-w-12 cursor-pointer border-0 bg-transparent p-0 text-center text-sm text-on-surface hover:text-primary\"\n type=\"button\"\n aria-label=\"Zoom presets\"\n [ngsMenuTriggerFor]=\"zoomMenu\">\n {{ zoomLabel() }}\n </button>\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Zoom in\"\n [disabled]=\"!canZoomIn()\"\n (click)=\"zoomIn()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:zoom-in-24-regular\" />\n </button>\n </div>\n\n <ngs-divider vertical fixedHeight />\n\n <div class=\"pdf-viewer-toolbar__cluster flex min-w-0 items-center gap-2\" aria-label=\"Page navigation\">\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Previous page\"\n [disabled]=\"!canGoPrevious()\"\n (click)=\"previousPage()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:chevron-left-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__page inline-flex h-9 min-w-16 items-center justify-center gap-2 rounded bg-surface-container-low text-sm font-bold text-on-surface\">\n <span>{{ activePage() }}</span>\n <span>/</span>\n <span>{{ pageCount() || 1 }}</span>\n </span>\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Next page\"\n [disabled]=\"!canGoNext()\"\n (click)=\"nextPage()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:chevron-right-24-regular\" />\n </button>\n </div>\n </ngs-toolbar-item>\n\n <ngs-toolbar-spacer />\n\n <ngs-toolbar-item class=\"pdf-viewer-toolbar__end flex min-w-0 flex-1 items-center justify-end gap-2\" aria-label=\"PDF tools\">\n @if (showSearchPanel()) {\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n [attr.aria-pressed]=\"isSearchPanelVisible()\"\n aria-label=\"Search\"\n [disabled]=\"!asidePanelInteractive()\"\n (click)=\"toggleSearchPanel()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:search-24-regular\" />\n </button>\n }\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"PDF viewer settings\"\n [ngsMenuTriggerFor]=\"settingsMenu\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:settings-24-regular\" />\n </button>\n @if (showAnnotationsPanel()) {\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n [attr.aria-pressed]=\"isAnnotationsPanelVisible()\"\n aria-label=\"Toggle annotations panel\"\n [disabled]=\"!asidePanelInteractive()\"\n (click)=\"toggleAnnotationsPanel()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:text-bullet-list-square-edit-24-regular\" />\n </button>\n }\n </ngs-toolbar-item>\n </ngs-toolbar>\n\n <ngs-menu #zoomMenu=\"ngsMenu\">\n @for (zoomPreset of zoomPresets; track zoomPreset) {\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"isZoomPresetSelected(zoomPreset)\"\n (click)=\"setZoomPreset(zoomPreset)\">\n <span>{{ zoomPreset * 100 }}%</span>\n </button>\n }\n\n <ngs-menu-divider />\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"zoomMode() === 'fit-page'\"\n (click)=\"fitToPage()\">\n <ngs-icon name=\"fluent:resize-large-24-regular\" />\n <span>Fit to Page</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"zoomMode() === 'fit-width'\"\n (click)=\"fitToWidth()\">\n <ngs-icon name=\"fluent:resize-24-regular\" />\n <span>Fit to Width</span>\n </button>\n </ngs-menu>\n\n <ngs-menu #settingsMenu=\"ngsMenu\" xPosition=\"before\">\n <ngs-menu-heading>SPREAD MODE</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'single'\"\n (click)=\"setSpreadMode('single')\">\n <ngs-icon name=\"fluent:document-24-regular\" />\n <span>Single Page</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-odd'\"\n (click)=\"setSpreadMode('two-odd')\">\n <ngs-icon name=\"fluent:book-open-24-regular\" />\n <span>Two Page (Odd)</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-even'\"\n (click)=\"setSpreadMode('two-even')\">\n <ngs-icon name=\"fluent:document-text-24-regular\" />\n <span>Two Page (Even)</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>SCROLL LAYOUT</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'vertical'\"\n (click)=\"setScrollLayout('vertical')\">\n <ngs-icon name=\"fluent:arrow-sort-24-regular\" />\n <span>Vertical</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'horizontal'\"\n (click)=\"setScrollLayout('horizontal')\">\n <ngs-icon name=\"fluent:arrow-swap-24-regular\" />\n <span>Horizontal</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>PAGE ROTATION</ngs-menu-heading>\n <button ngs-menu-item type=\"button\" (click)=\"rotateClockwise()\">\n <ngs-icon name=\"fluent:arrow-clockwise-24-regular\" />\n <span>Rotate Clockwise</span>\n </button>\n <button ngs-menu-item type=\"button\" (click)=\"rotateCounterClockwise()\">\n <ngs-icon name=\"fluent:arrow-counterclockwise-24-regular\" />\n <span>Rotate Counter-Clockwise</span>\n </button>\n\n <ngs-menu-divider />\n <button ngs-menu-item type=\"button\" (click)=\"toggleFullscreen()\">\n <ngs-icon name=\"fluent:arrow-maximize-24-regular\" />\n <span>Fullscreen</span>\n </button>\n </ngs-menu>\n </ngs-panel-header>\n }\n\n @if (isPageListVisible()) {\n <ngs-panel-sidebar class=\"w-[var(--ngs-pdf-viewer-sidebar-width)] min-w-[var(--ngs-pdf-viewer-sidebar-width)] border-r border-border bg-surface\">\n <div class=\"pdf-viewer-sidebar flex h-full min-h-0 flex-col gap-2 overflow-auto p-3\" #pageList>\n <div class=\"pdf-viewer-sidebar__title px-1 text-xs font-semibold text-on-surface-variant\">Pages</div>\n <div class=\"pdf-viewer-sidebar__pages flex min-h-0 flex-col gap-2\" aria-label=\"PDF pages\">\n @for (pageItem of pageItems(); track pageItem.pageNumber) {\n <button\n class=\"pdf-viewer-sidebar__page group flex w-full cursor-pointer flex-col items-stretch gap-1 rounded border border-transparent bg-transparent p-1 text-left text-on-surface-variant focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary [&.is-active]:text-on-surface\"\n type=\"button\"\n [class.is-active]=\"pageItem.pageNumber === activePage()\"\n [attr.aria-current]=\"pageItem.pageNumber === activePage() ? 'page' : null\"\n [attr.aria-label]=\"'Open page ' + pageItem.pageNumber\"\n [attr.data-ngs-pdf-page-button]=\"pageItem.pageNumber\"\n (click)=\"setPage(pageItem.pageNumber)\">\n <span class=\"pdf-viewer-sidebar__thumb flex aspect-[3/4] w-full items-center justify-center overflow-hidden rounded border border-border bg-surface shadow-xs group-hover:border-border\" aria-hidden=\"true\">\n @if (pageItem.thumbnail; as thumbnail) {\n <img\n class=\"block h-full w-full select-none object-contain\"\n [src]=\"thumbnail.url\"\n [attr.width]=\"thumbnail.width\"\n [attr.height]=\"thumbnail.height\"\n alt=\"\"\n draggable=\"false\" />\n } @else {\n <ngs-image-placeholder class=\"h-full w-full\" />\n }\n </span>\n <span class=\"pdf-viewer-sidebar__label block truncate text-center text-xs font-medium leading-tight\">Page {{ pageItem.pageNumber }}</span>\n </button>\n }\n </div>\n </div>\n </ngs-panel-sidebar>\n }\n\n <ngs-panel-content class=\"min-h-0 min-w-0 overflow-hidden bg-surface-container-low\">\n <div class=\"pdf-viewer-body absolute inset-0 overflow-auto\" #viewerBody (scroll)=\"onViewerScroll()\" (wheel)=\"onViewerWheel($event)\">\n <ngs-block-loader [loading]=\"isLoading()\">Loading PDF...</ngs-block-loader>\n @if (errorState()) {\n <div class=\"pdf-viewer-state pdf-viewer-state--error absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-danger\">\n <ngs-icon class=\"text-3xl\" name=\"fluent:error-circle-24-regular\" />\n <span>PDF could not be loaded.</span>\n </div>\n } @else if (!src()) {\n <div class=\"pdf-viewer-state absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-on-surface-variant\">\n <ngs-icon class=\"text-3xl\" name=\"fluent:document-pdf-24-regular\" />\n <span>No PDF selected.</span>\n </div>\n } @else {\n <div\n class=\"pdf-viewer-pages\"\n [class.is-horizontal]=\"scrollLayout() === 'horizontal'\"\n [class.is-vertical]=\"scrollLayout() === 'vertical'\"\n [class.is-spread]=\"spreadMode() !== 'single'\">\n @for (spread of pageSpreads(); track spread.id) {\n <div class=\"pdf-viewer-spread\" [attr.data-ngs-pdf-spread]=\"spread.id\">\n @if (spread.leadingPlaceholder) {\n @if (spread.leadingPlaceholderPage; as placeholderPage) {\n <div\n class=\"pdf-viewer-spread__placeholder\"\n [style.width.px]=\"placeholderPage.width\"\n [style.height.px]=\"placeholderPage.height\"\n aria-hidden=\"true\"></div>\n }\n }\n @for (pdfPage of spread.pages; track pdfPage.pageNumber) {\n <article\n class=\"pdf-viewer-page w-max max-w-none\"\n [attr.aria-label]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.data-ngs-pdf-page]=\"pdfPage.pageNumber\">\n <div\n class=\"pdf-viewer-page__surface relative cursor-text overflow-hidden rounded bg-surface shadow-md select-none touch-none\"\n [style.width.px]=\"pdfPage.width\"\n [style.height.px]=\"pdfPage.height\"\n (pointerdown)=\"startTextSelection($event, pdfPage)\"\n (pointermove)=\"updateTextSelection($event, pdfPage)\"\n (pointerup)=\"finishTextSelection($event, pdfPage)\"\n (pointercancel)=\"cancelTextSelection()\"\n (mousedown)=\"startTextSelection($event, pdfPage)\"\n (mousemove)=\"updateTextSelection($event, pdfPage)\"\n (mouseup)=\"finishTextSelection($event, pdfPage)\">\n @if (pdfPage.url; as pageUrl) {\n <img\n class=\"block h-full w-full max-w-none select-none pointer-events-none\"\n [src]=\"pageUrl\"\n [alt]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.width]=\"pdfPage.width\"\n [attr.height]=\"pdfPage.height\"\n draggable=\"false\" />\n } @else {\n <div class=\"pdf-viewer-page__placeholder pointer-events-none absolute inset-0 flex items-center justify-center bg-surface\" aria-hidden=\"true\">\n @if (pdfPage.isRendering) {\n <span class=\"size-8 animate-spin rounded-full border-2 border-border border-t-primary\"></span>\n }\n </div>\n }\n @if (selectionRectsForPage(pdfPage.pageNumber); as selectionRects) {\n @if (selectionRects.length > 0) {\n <div class=\"pdf-viewer-page__selection-layer pointer-events-none absolute inset-0 z-[1] isolate overflow-hidden mix-blend-multiply\" aria-hidden=\"true\">\n @for (selectionRect of selectionRects; track selectionRect.left + '-' + selectionRect.top + '-' + $index) {\n <div\n class=\"pdf-viewer-page__selection-rect pointer-events-none absolute bg-primary/30\"\n [style.left.px]=\"selectionRect.left\"\n [style.top.px]=\"selectionRect.top\"\n [style.width.px]=\"selectionRect.width\"\n [style.height.px]=\"selectionRect.height\"></div>\n }\n </div>\n }\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n }\n </div>\n </ngs-panel-content>\n\n @if (isSearchPanelVisible() || isAnnotationsPanelVisible()) {\n <ngs-panel-aside class=\"w-[var(--ngs-pdf-viewer-aside-width)] min-w-[var(--ngs-pdf-viewer-aside-width)] border-l border-border bg-surface\">\n @if (isSearchPanelVisible()) {\n <ngs-pdf-viewer-search\n [query]=\"activeSearchQuery()\"\n [results]=\"pdfSearchResults()\"\n (closed)=\"closeAsidePanel()\"\n (searchChanged)=\"updatePdfSearch($event)\"\n (resultSelected)=\"selectSearchResult($event)\" />\n } @else {\n <ngs-pdf-viewer-annotations\n [annotations]=\"annotationItems()\"\n [annotationDefs]=\"annotationDefs()\"\n [annotationTypeProperty]=\"annotationTypeProperty()\"\n (closed)=\"closeAsidePanel()\"\n (pageSelected)=\"setPage($event)\" />\n }\n </ngs-panel-aside>\n }\n</ngs-panel>\n", styles: [":host{--ngs-pdf-viewer-height: 720px;--ngs-pdf-viewer-min-height: 720px;--ngs-pdf-viewer-sidebar-width: 152px;--ngs-pdf-viewer-aside-width: 360px;--ngs-pdf-viewer-page-gap: 24px;--ngs-pdf-viewer-spread-gap: 16px;--ngs-pdf-viewer-page-padding: 24px;display:block;height:var(--ngs-pdf-viewer-height);min-height:var(--ngs-pdf-viewer-min-height);overflow:hidden}:host .pdf-viewer-pages{box-sizing:border-box;display:grid;gap:var(--ngs-pdf-viewer-page-gap);min-height:100%;min-width:100%;padding:var(--ngs-pdf-viewer-page-padding)}:host .pdf-viewer-pages.is-vertical{align-content:start;grid-auto-flow:row;grid-auto-rows:max-content;justify-items:center}:host .pdf-viewer-pages.is-horizontal{align-content:center;align-items:center;grid-auto-columns:max-content;grid-auto-flow:column;justify-content:start;width:max-content}:host .pdf-viewer-spread{align-items:start;display:grid;gap:var(--ngs-pdf-viewer-spread-gap);grid-auto-columns:max-content;grid-auto-flow:column;justify-content:center}:host .pdf-viewer-spread__placeholder{pointer-events:none;visibility:hidden}:host .pdf-viewer-toolbar{position:relative}:host .pdf-viewer-toolbar__center{left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);z-index:1}:host .pdf-viewer-toolbar__start,:host .pdf-viewer-toolbar__end{position:relative;z-index:2}:host .pdf-viewer-sidebar__page.is-active .pdf-viewer-sidebar__thumb{outline:2px solid var(--ngs-color-primary)}:host .pdf-viewer-page__selection-layer{forced-color-adjust:none}\n"], dependencies: [{ kind: "component", type: BlockLoader, selector: "ngs-block-loader", inputs: ["loading", "spinnerDiameter", "spinnerStrokeWidth"], exportAs: ["ngsBlockLoader"] }, { kind: "component", type: Button, selector: " button[ngsButton], button[ngsIconButton], a[ngsButton], a[ngsIconButton] ", inputs: ["ngsButton", "ngsIconButton", "loading", "disabled", "disabledInteractive", "disableRipple", "reverse", "fullWidth", "hideTextOnMobile"], exportAs: ["ngsButton"] }, { kind: "component", type: Divider, selector: "ngs-divider", inputs: ["vertical", "inset", "fixedHeight"], exportAs: ["ngsDivider"] }, { kind: "component", type: Icon, selector: "ngs-icon", inputs: ["name"], exportAs: ["ngsIcon"] }, { kind: "component", type: ImagePlaceholder, selector: "ngs-image-placeholder", exportAs: ["ngsImagePlaceholder"] }, { kind: "component", type: Menu, selector: "ngs-menu", inputs: ["role", "classList", "xPosition", "yPosition"], outputs: ["closed"], exportAs: ["ngsMenu"] }, { kind: "component", type: MenuDivider, selector: "ngs-menu-divider" }, { kind: "component", type: MenuHeading, selector: "ngs-menu-heading" }, { kind: "component", type: MenuItem, selector: "ngs-menu-item, [ngs-menu-item]", inputs: ["disabled", "role", "selected"], outputs: ["_triggered"], exportAs: ["ngsMenuItem"] }, { kind: "directive", type: MenuTrigger, selector: "[ngsMenuTriggerFor]", inputs: ["ngsMenuTriggerFor", "ngsMenuTriggerData", "ngsMenuDisabled", "xPosition", "yPosition", "ngsMenuTriggerRestoreFocus"], outputs: ["menuOpened", "menuClosed"], exportAs: ["ngsMenuTrigger"] }, { kind: "component", type: Panel, selector: "ngs-panel", inputs: ["absolute"], exportAs: ["ngsPanel"] }, { kind: "component", type: PanelAside, selector: "ngs-panel-aside" }, { kind: "component", type: PanelContent, selector: "ngs-panel-content", exportAs: ["ngsPanelContent"] }, { kind: "component", type: PanelHeader, selector: "ngs-panel-header", inputs: ["flex", "autoHeight"], exportAs: ["ngsPanelHeader"] }, { kind: "component", type: PanelSidebar, selector: "ngs-panel-sidebar" }, { kind: "component", type: Toolbar, selector: "ngs-toolbar", exportAs: ["ngsToolbar"] }, { kind: "component", type: ToolbarItem, selector: "ngs-toolbar-item", inputs: ["hidden"], outputs: ["hiddenChange"] }, { kind: "component", type: ToolbarSpacer, selector: "ngs-toolbar-spacer" }, { kind: "component", type: ToolbarTitle, selector: "ngs-toolbar-title" }, { kind: "component", type: PdfViewerAnnotations, selector: "ngs-pdf-viewer-annotations", inputs: ["annotations", "annotationDefs", "annotationTypeProperty"], outputs: ["closed", "pageSelected"] }, { kind: "component", type: PdfViewerSearch, selector: "ngs-pdf-viewer-search", inputs: ["results", "query"], outputs: ["closed", "resultSelected", "searchChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1112
1967
|
}
|
|
1113
1968
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewer, decorators: [{
|
|
1114
1969
|
type: Component,
|
|
1115
|
-
args: [{ selector: 'ngs-pdf-viewer', exportAs: 'ngsPdfViewer',
|
|
1970
|
+
args: [{ selector: 'ngs-pdf-viewer', exportAs: 'ngsPdfViewer', imports: [
|
|
1116
1971
|
BlockLoader,
|
|
1117
1972
|
Button,
|
|
1973
|
+
Divider,
|
|
1118
1974
|
Icon,
|
|
1119
1975
|
ImagePlaceholder,
|
|
1120
1976
|
Menu,
|
|
@@ -1123,21 +1979,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
1123
1979
|
MenuItem,
|
|
1124
1980
|
MenuTrigger,
|
|
1125
1981
|
Panel,
|
|
1982
|
+
PanelAside,
|
|
1126
1983
|
PanelContent,
|
|
1127
1984
|
PanelHeader,
|
|
1128
1985
|
PanelSidebar,
|
|
1986
|
+
Toolbar,
|
|
1987
|
+
ToolbarItem,
|
|
1988
|
+
ToolbarSpacer,
|
|
1989
|
+
ToolbarTitle,
|
|
1990
|
+
PdfViewerAnnotations,
|
|
1991
|
+
PdfViewerSearch,
|
|
1129
1992
|
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1130
1993
|
class: 'ngs-pdf-viewer not-prose',
|
|
1131
1994
|
'[class.is-loading]': 'isLoading()',
|
|
1132
1995
|
'[class.has-toolbar]': 'showToolbar()',
|
|
1133
1996
|
'[class.has-page-list]': 'isPageListVisible()',
|
|
1134
1997
|
'[class.has-error]': 'errorState()',
|
|
1135
|
-
}, template: "<ngs-panel class=\"pdf-viewer-panel\">\n @if (showToolbar()) {\n <ngs-panel-header flex>\n <div class=\"pdf-viewer-toolbar\" aria-label=\"PDF viewer controls\">\n @if (showPageList()) {\n <button\n ngsIconButton\n type=\"button\"\n [attr.aria-label]=\"pageListVisible() ? 'Hide pages panel' : 'Show pages panel'\"\n [attr.aria-pressed]=\"pageListVisible()\"\n (click)=\"togglePageList()\">\n <ngs-icon\n [name]=\"pageListVisible() ? 'fluent:panel-left-contract-24-regular' : 'fluent:panel-left-expand-24-regular'\" />\n </button>\n }\n <button ngsIconButton type=\"button\" aria-label=\"Previous page\" [disabled]=\"!canGoPrevious()\" (click)=\"previousPage()\">\n <ngs-icon name=\"fluent:chevron-up-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__page\">{{ activePage() }} / {{ pageCount() || 1 }}</span>\n <button ngsIconButton type=\"button\" aria-label=\"Next page\" [disabled]=\"!canGoNext()\" (click)=\"nextPage()\">\n <ngs-icon name=\"fluent:chevron-down-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__spacer\"></span>\n <button ngsIconButton type=\"button\" aria-label=\"Zoom out\" [disabled]=\"!canZoomOut()\" (click)=\"zoomOut()\">\n <ngs-icon name=\"fluent:zoom-out-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__zoom\">{{ zoomLabel() }}</span>\n <button ngsIconButton type=\"button\" aria-label=\"Zoom in\" [disabled]=\"!canZoomIn()\" (click)=\"zoomIn()\">\n <ngs-icon name=\"fluent:zoom-in-24-regular\" />\n </button>\n <button ngsIconButton type=\"button\" aria-label=\"PDF viewer settings\" [ngsMenuTriggerFor]=\"settingsMenu\">\n <ngs-icon name=\"fluent:settings-24-regular\" />\n </button>\n </div>\n\n <ngs-menu #settingsMenu=\"ngsMenu\" xPosition=\"before\">\n <ngs-menu-heading>SPREAD MODE</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'single'\"\n (click)=\"setSpreadMode('single')\">\n <ngs-icon name=\"fluent:document-24-regular\" />\n <span>Single Page</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-odd'\"\n (click)=\"setSpreadMode('two-odd')\">\n <ngs-icon name=\"fluent:book-open-24-regular\" />\n <span>Two Page (Odd)</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-even'\"\n (click)=\"setSpreadMode('two-even')\">\n <ngs-icon name=\"fluent:document-text-24-regular\" />\n <span>Two Page (Even)</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>SCROLL LAYOUT</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'vertical'\"\n (click)=\"setScrollLayout('vertical')\">\n <ngs-icon name=\"fluent:arrow-sort-24-regular\" />\n <span>Vertical</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'horizontal'\"\n (click)=\"setScrollLayout('horizontal')\">\n <ngs-icon name=\"fluent:arrow-swap-24-regular\" />\n <span>Horizontal</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>PAGE ROTATION</ngs-menu-heading>\n <button ngs-menu-item type=\"button\" (click)=\"rotateClockwise()\">\n <ngs-icon name=\"fluent:arrow-clockwise-24-regular\" />\n <span>Rotate Clockwise</span>\n </button>\n <button ngs-menu-item type=\"button\" (click)=\"rotateCounterClockwise()\">\n <ngs-icon name=\"fluent:arrow-counterclockwise-24-regular\" />\n <span>Rotate Counter-Clockwise</span>\n </button>\n\n <ngs-menu-divider />\n <button ngs-menu-item type=\"button\" (click)=\"toggleFullscreen()\">\n <ngs-icon name=\"fluent:arrow-maximize-24-regular\" />\n <span>Fullscreen</span>\n </button>\n </ngs-menu>\n </ngs-panel-header>\n }\n\n @if (isPageListVisible()) {\n <ngs-panel-sidebar>\n <div class=\"pdf-viewer-sidebar\" #pageList>\n <div class=\"pdf-viewer-sidebar__title\">Pages</div>\n <div class=\"pdf-viewer-sidebar__pages\" aria-label=\"PDF pages\">\n @for (pageItem of pageItems(); track pageItem.pageNumber) {\n <button\n class=\"pdf-viewer-sidebar__page\"\n type=\"button\"\n [class.is-active]=\"pageItem.pageNumber === activePage()\"\n [attr.aria-current]=\"pageItem.pageNumber === activePage() ? 'page' : null\"\n [attr.aria-label]=\"'Open page ' + pageItem.pageNumber\"\n [attr.data-ngs-pdf-page-button]=\"pageItem.pageNumber\"\n (click)=\"setPage(pageItem.pageNumber)\">\n <span class=\"pdf-viewer-sidebar__thumb\" aria-hidden=\"true\">\n @if (pageItem.thumbnail; as thumbnail) {\n <img\n [src]=\"thumbnail.url\"\n [attr.width]=\"thumbnail.width\"\n [attr.height]=\"thumbnail.height\"\n alt=\"\"\n draggable=\"false\" />\n } @else {\n <ngs-image-placeholder />\n }\n </span>\n <span class=\"pdf-viewer-sidebar__label\">Page {{ pageItem.pageNumber }}</span>\n </button>\n }\n </div>\n </div>\n </ngs-panel-sidebar>\n }\n\n <ngs-panel-content>\n <div class=\"pdf-viewer-body\" #viewerBody (scroll)=\"onViewerScroll()\">\n <ngs-block-loader [loading]=\"isLoading()\">Loading PDF...</ngs-block-loader>\n @if (errorState()) {\n <div class=\"pdf-viewer-state pdf-viewer-state--error\">\n <ngs-icon name=\"fluent:error-circle-24-regular\" />\n <span>PDF could not be loaded.</span>\n </div>\n } @else if (!src()) {\n <div class=\"pdf-viewer-state\">\n <ngs-icon name=\"fluent:document-pdf-24-regular\" />\n <span>No PDF selected.</span>\n </div>\n } @else {\n <div class=\"pdf-viewer-pages\">\n @for (pdfPage of renderedPages(); track pdfPage.pageNumber) {\n <article\n class=\"pdf-viewer-page\"\n [attr.aria-label]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.data-ngs-pdf-page]=\"pdfPage.pageNumber\">\n <div\n class=\"pdf-viewer-page__surface\"\n [style.width.px]=\"pdfPage.width\"\n [style.height.px]=\"pdfPage.height\"\n (pointerdown)=\"startTextSelection($event, pdfPage)\"\n (pointermove)=\"updateTextSelection($event, pdfPage)\"\n (pointerup)=\"finishTextSelection($event, pdfPage)\"\n (pointercancel)=\"cancelTextSelection()\"\n (mousedown)=\"startTextSelection($event, pdfPage)\"\n (mousemove)=\"updateTextSelection($event, pdfPage)\"\n (mouseup)=\"finishTextSelection($event, pdfPage)\">\n @if (isPageImageFresh(pdfPage) && pdfPage.url; as pageUrl) {\n <img\n [src]=\"pageUrl\"\n [alt]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.width]=\"pdfPage.width\"\n [attr.height]=\"pdfPage.height\"\n draggable=\"false\" />\n } @else {\n <div class=\"pdf-viewer-page__placeholder\" aria-hidden=\"true\">\n @if (pdfPage.isRendering) {\n <span></span>\n }\n </div>\n }\n @if (selectionRectsForPage(pdfPage.pageNumber); as selectionRects) {\n @if (selectionRects.length > 0) {\n <div class=\"pdf-viewer-page__selection-layer\" aria-hidden=\"true\">\n @for (selectionRect of selectionRects; track selectionRect.left + '-' + selectionRect.top + '-' + $index) {\n <div\n class=\"pdf-viewer-page__selection-rect\"\n [style.left.px]=\"selectionRect.left\"\n [style.top.px]=\"selectionRect.top\"\n [style.width.px]=\"selectionRect.width\"\n [style.height.px]=\"selectionRect.height\"></div>\n }\n </div>\n }\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n </ngs-panel-content>\n</ngs-panel>\n", styles: [":host{--ngs-pdf-viewer-height: 640px;--ngs-pdf-viewer-background: var(--ngs-color-surface-container-low, #f5f6f8);--ngs-pdf-viewer-page-background: var(--ngs-color-surface, #ffffff);--ngs-pdf-viewer-page-shadow: var(--ngs-shadow-md, 0 8px 24px rgba(15, 23, 42, .12));--ngs-pdf-viewer-border-color: var(--ngs-color-outline-variant, rgba(15, 23, 42, .12));--ngs-pdf-viewer-radius: var(--ngs-radius-md, 10px);--ngs-pdf-viewer-sidebar-width: 152px;--ngs-pdf-viewer-selection-background: rgb(33 150 243 / .34);display:block;height:var(--ngs-pdf-viewer-height);min-height:320px;overflow:hidden;border:1px solid var(--ngs-pdf-viewer-border-color);border-radius:var(--ngs-pdf-viewer-radius);background:var(--ngs-pdf-viewer-background);color:var(--ngs-color-on-surface, #111827)}:host ngs-panel{height:100%;overflow:hidden;border-radius:inherit;background:var(--ngs-pdf-viewer-background)}:host ngs-panel-header{min-width:0;border-bottom:1px solid var(--ngs-pdf-viewer-border-color);background:var(--ngs-color-surface, #ffffff)}:host ngs-panel-sidebar{width:var(--ngs-pdf-viewer-sidebar-width);min-width:var(--ngs-pdf-viewer-sidebar-width);border-right:1px solid var(--ngs-pdf-viewer-border-color);background:var(--ngs-color-surface, #ffffff)}:host ngs-panel-content{min-width:0;min-height:0;overflow:hidden;background:var(--ngs-pdf-viewer-background)}:host .pdf-viewer-toolbar{display:flex;align-items:center;gap:var(--ngs-spacing-1, .25rem);width:100%;padding:0 var(--ngs-spacing-2, .5rem)}:host .pdf-viewer-toolbar__page,:host .pdf-viewer-toolbar__zoom{min-width:76px;text-align:center;font-size:var(--ngs-font-size-sm, .875rem);font-weight:500;color:var(--ngs-color-on-surface-variant, #4b5563)}:host .pdf-viewer-toolbar__spacer{flex:1 1 auto}:host .pdf-viewer-sidebar{display:flex;flex-direction:column;gap:var(--ngs-spacing-2, .5rem);height:100%;min-height:0;padding:var(--ngs-spacing-3, .75rem);overflow:auto}:host .pdf-viewer-sidebar__title{padding:0 var(--ngs-spacing-1, .25rem);font-size:var(--ngs-font-size-xs, .75rem);font-weight:600;color:var(--ngs-color-on-surface-variant, #6b7280)}:host .pdf-viewer-sidebar__pages{display:flex;flex-direction:column;gap:var(--ngs-spacing-2, .5rem);min-height:0}:host .pdf-viewer-sidebar__page{display:flex;flex-direction:column;align-items:stretch;gap:var(--ngs-spacing-1, .25rem);width:100%;padding:var(--ngs-spacing-1, .25rem);border:1px solid transparent;border-radius:calc(var(--ngs-pdf-viewer-radius) - 4px);background:transparent;color:var(--ngs-color-on-surface-variant, #4b5563);cursor:pointer;text-align:left}:host .pdf-viewer-sidebar__page:hover .pdf-viewer-sidebar__thumb{border-color:var(--ngs-color-outline, rgba(15, 23, 42, .24))}:host .pdf-viewer-sidebar__page:focus-visible{outline:2px solid var(--ngs-color-primary, #155eef);outline-offset:2px}:host .pdf-viewer-sidebar__page.is-active{color:var(--ngs-color-on-surface, #111827)}:host .pdf-viewer-sidebar__page.is-active .pdf-viewer-sidebar__thumb{border-color:var(--ngs-color-primary, #155eef);background:var(--ngs-color-primary-container, rgba(21, 94, 239, .08));box-shadow:0 0 0 2px var(--ngs-color-primary-container, rgba(21, 94, 239, .14)),var(--ngs-shadow-xs, 0 1px 2px rgba(15, 23, 42, .08))}:host .pdf-viewer-sidebar__thumb{display:flex;align-items:center;justify-content:center;aspect-ratio:3/4;width:100%;overflow:hidden;border:1px solid var(--ngs-pdf-viewer-border-color);border-radius:calc(var(--ngs-pdf-viewer-radius) - 6px);background:var(--ngs-pdf-viewer-page-background);box-shadow:var(--ngs-shadow-xs, 0 1px 2px rgba(15, 23, 42, .08))}:host .pdf-viewer-sidebar__thumb img{display:block;width:100%;height:100%;object-fit:contain;-webkit-user-select:none;user-select:none}:host .pdf-viewer-sidebar__thumb ngs-image-placeholder{width:100%;height:100%}:host .pdf-viewer-sidebar__thumb ngs-image-placeholder ::ng-deep svg{width:64%;min-width:0}:host .pdf-viewer-sidebar__thumb ngs-icon{font-size:1.375rem;color:var(--ngs-color-on-surface-variant, #6b7280)}:host .pdf-viewer-sidebar__label{display:block;overflow:hidden;font-size:var(--ngs-font-size-xs, .75rem);font-weight:500;line-height:1.25;text-align:center;text-overflow:ellipsis;white-space:nowrap}:host .pdf-viewer-body{position:absolute;inset:0;overflow:auto}:host .pdf-viewer-pages{display:flex;flex-direction:column;align-items:center;gap:var(--ngs-spacing-6, 1.5rem);min-height:100%;padding:var(--ngs-spacing-6, 1.5rem)}:host .pdf-viewer-page{width:max-content;max-width:none}:host .pdf-viewer-page__surface{position:relative;overflow:hidden;cursor:text;-webkit-user-select:none;user-select:none;border-radius:calc(var(--ngs-pdf-viewer-radius) - 4px);background:var(--ngs-pdf-viewer-page-background);box-shadow:var(--ngs-pdf-viewer-page-shadow);touch-action:none}:host .pdf-viewer-page__surface img{display:block;width:100%;height:100%;max-width:none;-webkit-user-select:none;user-select:none;pointer-events:none}:host .pdf-viewer-page__placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--ngs-pdf-viewer-page-background);pointer-events:none}:host .pdf-viewer-page__placeholder span{width:32px;height:32px;border:2px solid var(--ngs-color-outline-variant, rgba(15, 23, 42, .12));border-top-color:var(--ngs-color-primary, #155eef);border-radius:999px;animation:ngs-pdf-viewer-page-spin .8s linear infinite}:host .pdf-viewer-page__selection-layer{position:absolute;inset:0;z-index:1;overflow:hidden;isolation:isolate;mix-blend-mode:multiply;forced-color-adjust:none;pointer-events:none}:host .pdf-viewer-page__selection-rect{position:absolute;background:var(--ngs-pdf-viewer-selection-background);pointer-events:none}:host .pdf-viewer-state{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--ngs-spacing-3, .75rem);padding:var(--ngs-spacing-6, 1.5rem);color:var(--ngs-color-on-surface-variant, #6b7280);text-align:center}:host .pdf-viewer-state ngs-icon{font-size:2rem}:host .pdf-viewer-state--error{color:var(--ngs-color-danger, #dc2626)}@keyframes ngs-pdf-viewer-page-spin{to{transform:rotate(360deg)}}\n"] }]
|
|
1136
|
-
}], ctorParameters: () => [], propDecorators: { viewerBody: [{ type: i0.ViewChild, args: ['viewerBody', { isSignal: true }] }], pageList: [{ type: i0.ViewChild, args: ['pageList', { isSignal: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], wasmUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "wasmUrl", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], scale: [{ type: i0.Input, args: [{ isSignal: true, alias: "scale", required: false }] }], minScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "minScale", required: false }] }], maxScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxScale", required: false }] }], zoomStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomStep", required: false }] }], renderAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderAll", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showPageList: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPageList", required: false }] }], withAnnotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "withAnnotations", required: false }] }], withForms: [{ type: i0.Input, args: [{ isSignal: true, alias: "withForms", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], pageChanged: [{ type: i0.Output, args: ["pageChanged"] }], pageRendered: [{ type: i0.Output, args: ["pageRendered"] }], error: [{ type: i0.Output, args: ["error"] }] } });
|
|
1998
|
+
}, template: "<ngs-panel class=\"pdf-viewer-panel h-full overflow-hidden rounded-lg border border-border bg-surface-container-low text-on-surface\">\n @if (showToolbar()) {\n <ngs-panel-header class=\"min-w-0 border-b border-border bg-surface\" flex>\n <ngs-toolbar class=\"pdf-viewer-toolbar flex h-14 w-full min-w-0 items-center gap-4 overflow-visible px-2 text-on-surface-variant\" aria-label=\"PDF viewer controls\">\n <ngs-toolbar-item class=\"pdf-viewer-toolbar__start flex min-w-0 flex-none items-center gap-2\">\n @if (showPageList()) {\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n [attr.aria-label]=\"pageListVisible() ? 'Hide pages panel' : 'Show pages panel'\"\n [attr.aria-pressed]=\"pageListVisible()\"\n (click)=\"togglePageList()\">\n <ngs-icon\n class=\"text-2xl\"\n [name]=\"pageListVisible() ? 'fluent:panel-left-contract-24-regular' : 'fluent:panel-left-expand-24-regular'\" />\n </button>\n }\n <ngs-divider vertical fixedHeight />\n </ngs-toolbar-item>\n <ngs-toolbar-title class=\"min-w-0 flex-1 truncate text-sm! max-w-[20%] font-semibold! text-on-surface-variant\" [title]=\"displayDocumentName()\">\n {{ displayDocumentName() }}\n </ngs-toolbar-title>\n\n <ngs-toolbar-item class=\"pdf-viewer-toolbar__center flex flex-none items-center gap-3\">\n <div class=\"pdf-viewer-toolbar__cluster flex min-w-0 items-center gap-2\" aria-label=\"Zoom controls\">\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Zoom out\"\n [disabled]=\"!canZoomOut()\"\n (click)=\"zoomOut()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:zoom-out-24-regular\" />\n </button>\n <button\n class=\"pdf-viewer-toolbar__zoom min-w-12 cursor-pointer border-0 bg-transparent p-0 text-center text-sm text-on-surface hover:text-primary\"\n type=\"button\"\n aria-label=\"Zoom presets\"\n [ngsMenuTriggerFor]=\"zoomMenu\">\n {{ zoomLabel() }}\n </button>\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Zoom in\"\n [disabled]=\"!canZoomIn()\"\n (click)=\"zoomIn()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:zoom-in-24-regular\" />\n </button>\n </div>\n\n <ngs-divider vertical fixedHeight />\n\n <div class=\"pdf-viewer-toolbar__cluster flex min-w-0 items-center gap-2\" aria-label=\"Page navigation\">\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Previous page\"\n [disabled]=\"!canGoPrevious()\"\n (click)=\"previousPage()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:chevron-left-24-regular\" />\n </button>\n <span class=\"pdf-viewer-toolbar__page inline-flex h-9 min-w-16 items-center justify-center gap-2 rounded bg-surface-container-low text-sm font-bold text-on-surface\">\n <span>{{ activePage() }}</span>\n <span>/</span>\n <span>{{ pageCount() || 1 }}</span>\n </span>\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"Next page\"\n [disabled]=\"!canGoNext()\"\n (click)=\"nextPage()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:chevron-right-24-regular\" />\n </button>\n </div>\n </ngs-toolbar-item>\n\n <ngs-toolbar-spacer />\n\n <ngs-toolbar-item class=\"pdf-viewer-toolbar__end flex min-w-0 flex-1 items-center justify-end gap-2\" aria-label=\"PDF tools\">\n @if (showSearchPanel()) {\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n [attr.aria-pressed]=\"isSearchPanelVisible()\"\n aria-label=\"Search\"\n [disabled]=\"!asidePanelInteractive()\"\n (click)=\"toggleSearchPanel()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:search-24-regular\" />\n </button>\n }\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n aria-label=\"PDF viewer settings\"\n [ngsMenuTriggerFor]=\"settingsMenu\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:settings-24-regular\" />\n </button>\n @if (showAnnotationsPanel()) {\n <button\n class=\"pdf-viewer-toolbar__button size-10 text-on-surface-variant hover:bg-surface-container-low hover:text-on-surface\"\n ngsIconButton\n type=\"button\"\n [attr.aria-pressed]=\"isAnnotationsPanelVisible()\"\n aria-label=\"Toggle annotations panel\"\n [disabled]=\"!asidePanelInteractive()\"\n (click)=\"toggleAnnotationsPanel()\">\n <ngs-icon class=\"text-2xl\" name=\"fluent:text-bullet-list-square-edit-24-regular\" />\n </button>\n }\n </ngs-toolbar-item>\n </ngs-toolbar>\n\n <ngs-menu #zoomMenu=\"ngsMenu\">\n @for (zoomPreset of zoomPresets; track zoomPreset) {\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"isZoomPresetSelected(zoomPreset)\"\n (click)=\"setZoomPreset(zoomPreset)\">\n <span>{{ zoomPreset * 100 }}%</span>\n </button>\n }\n\n <ngs-menu-divider />\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"zoomMode() === 'fit-page'\"\n (click)=\"fitToPage()\">\n <ngs-icon name=\"fluent:resize-large-24-regular\" />\n <span>Fit to Page</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"zoomMode() === 'fit-width'\"\n (click)=\"fitToWidth()\">\n <ngs-icon name=\"fluent:resize-24-regular\" />\n <span>Fit to Width</span>\n </button>\n </ngs-menu>\n\n <ngs-menu #settingsMenu=\"ngsMenu\" xPosition=\"before\">\n <ngs-menu-heading>SPREAD MODE</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'single'\"\n (click)=\"setSpreadMode('single')\">\n <ngs-icon name=\"fluent:document-24-regular\" />\n <span>Single Page</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-odd'\"\n (click)=\"setSpreadMode('two-odd')\">\n <ngs-icon name=\"fluent:book-open-24-regular\" />\n <span>Two Page (Odd)</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"spreadMode() === 'two-even'\"\n (click)=\"setSpreadMode('two-even')\">\n <ngs-icon name=\"fluent:document-text-24-regular\" />\n <span>Two Page (Even)</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>SCROLL LAYOUT</ngs-menu-heading>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'vertical'\"\n (click)=\"setScrollLayout('vertical')\">\n <ngs-icon name=\"fluent:arrow-sort-24-regular\" />\n <span>Vertical</span>\n </button>\n <button\n ngs-menu-item\n type=\"button\"\n [selected]=\"scrollLayout() === 'horizontal'\"\n (click)=\"setScrollLayout('horizontal')\">\n <ngs-icon name=\"fluent:arrow-swap-24-regular\" />\n <span>Horizontal</span>\n </button>\n\n <ngs-menu-divider />\n <ngs-menu-heading>PAGE ROTATION</ngs-menu-heading>\n <button ngs-menu-item type=\"button\" (click)=\"rotateClockwise()\">\n <ngs-icon name=\"fluent:arrow-clockwise-24-regular\" />\n <span>Rotate Clockwise</span>\n </button>\n <button ngs-menu-item type=\"button\" (click)=\"rotateCounterClockwise()\">\n <ngs-icon name=\"fluent:arrow-counterclockwise-24-regular\" />\n <span>Rotate Counter-Clockwise</span>\n </button>\n\n <ngs-menu-divider />\n <button ngs-menu-item type=\"button\" (click)=\"toggleFullscreen()\">\n <ngs-icon name=\"fluent:arrow-maximize-24-regular\" />\n <span>Fullscreen</span>\n </button>\n </ngs-menu>\n </ngs-panel-header>\n }\n\n @if (isPageListVisible()) {\n <ngs-panel-sidebar class=\"w-[var(--ngs-pdf-viewer-sidebar-width)] min-w-[var(--ngs-pdf-viewer-sidebar-width)] border-r border-border bg-surface\">\n <div class=\"pdf-viewer-sidebar flex h-full min-h-0 flex-col gap-2 overflow-auto p-3\" #pageList>\n <div class=\"pdf-viewer-sidebar__title px-1 text-xs font-semibold text-on-surface-variant\">Pages</div>\n <div class=\"pdf-viewer-sidebar__pages flex min-h-0 flex-col gap-2\" aria-label=\"PDF pages\">\n @for (pageItem of pageItems(); track pageItem.pageNumber) {\n <button\n class=\"pdf-viewer-sidebar__page group flex w-full cursor-pointer flex-col items-stretch gap-1 rounded border border-transparent bg-transparent p-1 text-left text-on-surface-variant focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary [&.is-active]:text-on-surface\"\n type=\"button\"\n [class.is-active]=\"pageItem.pageNumber === activePage()\"\n [attr.aria-current]=\"pageItem.pageNumber === activePage() ? 'page' : null\"\n [attr.aria-label]=\"'Open page ' + pageItem.pageNumber\"\n [attr.data-ngs-pdf-page-button]=\"pageItem.pageNumber\"\n (click)=\"setPage(pageItem.pageNumber)\">\n <span class=\"pdf-viewer-sidebar__thumb flex aspect-[3/4] w-full items-center justify-center overflow-hidden rounded border border-border bg-surface shadow-xs group-hover:border-border\" aria-hidden=\"true\">\n @if (pageItem.thumbnail; as thumbnail) {\n <img\n class=\"block h-full w-full select-none object-contain\"\n [src]=\"thumbnail.url\"\n [attr.width]=\"thumbnail.width\"\n [attr.height]=\"thumbnail.height\"\n alt=\"\"\n draggable=\"false\" />\n } @else {\n <ngs-image-placeholder class=\"h-full w-full\" />\n }\n </span>\n <span class=\"pdf-viewer-sidebar__label block truncate text-center text-xs font-medium leading-tight\">Page {{ pageItem.pageNumber }}</span>\n </button>\n }\n </div>\n </div>\n </ngs-panel-sidebar>\n }\n\n <ngs-panel-content class=\"min-h-0 min-w-0 overflow-hidden bg-surface-container-low\">\n <div class=\"pdf-viewer-body absolute inset-0 overflow-auto\" #viewerBody (scroll)=\"onViewerScroll()\" (wheel)=\"onViewerWheel($event)\">\n <ngs-block-loader [loading]=\"isLoading()\">Loading PDF...</ngs-block-loader>\n @if (errorState()) {\n <div class=\"pdf-viewer-state pdf-viewer-state--error absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-danger\">\n <ngs-icon class=\"text-3xl\" name=\"fluent:error-circle-24-regular\" />\n <span>PDF could not be loaded.</span>\n </div>\n } @else if (!src()) {\n <div class=\"pdf-viewer-state absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-on-surface-variant\">\n <ngs-icon class=\"text-3xl\" name=\"fluent:document-pdf-24-regular\" />\n <span>No PDF selected.</span>\n </div>\n } @else {\n <div\n class=\"pdf-viewer-pages\"\n [class.is-horizontal]=\"scrollLayout() === 'horizontal'\"\n [class.is-vertical]=\"scrollLayout() === 'vertical'\"\n [class.is-spread]=\"spreadMode() !== 'single'\">\n @for (spread of pageSpreads(); track spread.id) {\n <div class=\"pdf-viewer-spread\" [attr.data-ngs-pdf-spread]=\"spread.id\">\n @if (spread.leadingPlaceholder) {\n @if (spread.leadingPlaceholderPage; as placeholderPage) {\n <div\n class=\"pdf-viewer-spread__placeholder\"\n [style.width.px]=\"placeholderPage.width\"\n [style.height.px]=\"placeholderPage.height\"\n aria-hidden=\"true\"></div>\n }\n }\n @for (pdfPage of spread.pages; track pdfPage.pageNumber) {\n <article\n class=\"pdf-viewer-page w-max max-w-none\"\n [attr.aria-label]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.data-ngs-pdf-page]=\"pdfPage.pageNumber\">\n <div\n class=\"pdf-viewer-page__surface relative cursor-text overflow-hidden rounded bg-surface shadow-md select-none touch-none\"\n [style.width.px]=\"pdfPage.width\"\n [style.height.px]=\"pdfPage.height\"\n (pointerdown)=\"startTextSelection($event, pdfPage)\"\n (pointermove)=\"updateTextSelection($event, pdfPage)\"\n (pointerup)=\"finishTextSelection($event, pdfPage)\"\n (pointercancel)=\"cancelTextSelection()\"\n (mousedown)=\"startTextSelection($event, pdfPage)\"\n (mousemove)=\"updateTextSelection($event, pdfPage)\"\n (mouseup)=\"finishTextSelection($event, pdfPage)\">\n @if (pdfPage.url; as pageUrl) {\n <img\n class=\"block h-full w-full max-w-none select-none pointer-events-none\"\n [src]=\"pageUrl\"\n [alt]=\"'PDF page ' + pdfPage.pageNumber\"\n [attr.width]=\"pdfPage.width\"\n [attr.height]=\"pdfPage.height\"\n draggable=\"false\" />\n } @else {\n <div class=\"pdf-viewer-page__placeholder pointer-events-none absolute inset-0 flex items-center justify-center bg-surface\" aria-hidden=\"true\">\n @if (pdfPage.isRendering) {\n <span class=\"size-8 animate-spin rounded-full border-2 border-border border-t-primary\"></span>\n }\n </div>\n }\n @if (selectionRectsForPage(pdfPage.pageNumber); as selectionRects) {\n @if (selectionRects.length > 0) {\n <div class=\"pdf-viewer-page__selection-layer pointer-events-none absolute inset-0 z-[1] isolate overflow-hidden mix-blend-multiply\" aria-hidden=\"true\">\n @for (selectionRect of selectionRects; track selectionRect.left + '-' + selectionRect.top + '-' + $index) {\n <div\n class=\"pdf-viewer-page__selection-rect pointer-events-none absolute bg-primary/30\"\n [style.left.px]=\"selectionRect.left\"\n [style.top.px]=\"selectionRect.top\"\n [style.width.px]=\"selectionRect.width\"\n [style.height.px]=\"selectionRect.height\"></div>\n }\n </div>\n }\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n }\n </div>\n </ngs-panel-content>\n\n @if (isSearchPanelVisible() || isAnnotationsPanelVisible()) {\n <ngs-panel-aside class=\"w-[var(--ngs-pdf-viewer-aside-width)] min-w-[var(--ngs-pdf-viewer-aside-width)] border-l border-border bg-surface\">\n @if (isSearchPanelVisible()) {\n <ngs-pdf-viewer-search\n [query]=\"activeSearchQuery()\"\n [results]=\"pdfSearchResults()\"\n (closed)=\"closeAsidePanel()\"\n (searchChanged)=\"updatePdfSearch($event)\"\n (resultSelected)=\"selectSearchResult($event)\" />\n } @else {\n <ngs-pdf-viewer-annotations\n [annotations]=\"annotationItems()\"\n [annotationDefs]=\"annotationDefs()\"\n [annotationTypeProperty]=\"annotationTypeProperty()\"\n (closed)=\"closeAsidePanel()\"\n (pageSelected)=\"setPage($event)\" />\n }\n </ngs-panel-aside>\n }\n</ngs-panel>\n", styles: [":host{--ngs-pdf-viewer-height: 720px;--ngs-pdf-viewer-min-height: 720px;--ngs-pdf-viewer-sidebar-width: 152px;--ngs-pdf-viewer-aside-width: 360px;--ngs-pdf-viewer-page-gap: 24px;--ngs-pdf-viewer-spread-gap: 16px;--ngs-pdf-viewer-page-padding: 24px;display:block;height:var(--ngs-pdf-viewer-height);min-height:var(--ngs-pdf-viewer-min-height);overflow:hidden}:host .pdf-viewer-pages{box-sizing:border-box;display:grid;gap:var(--ngs-pdf-viewer-page-gap);min-height:100%;min-width:100%;padding:var(--ngs-pdf-viewer-page-padding)}:host .pdf-viewer-pages.is-vertical{align-content:start;grid-auto-flow:row;grid-auto-rows:max-content;justify-items:center}:host .pdf-viewer-pages.is-horizontal{align-content:center;align-items:center;grid-auto-columns:max-content;grid-auto-flow:column;justify-content:start;width:max-content}:host .pdf-viewer-spread{align-items:start;display:grid;gap:var(--ngs-pdf-viewer-spread-gap);grid-auto-columns:max-content;grid-auto-flow:column;justify-content:center}:host .pdf-viewer-spread__placeholder{pointer-events:none;visibility:hidden}:host .pdf-viewer-toolbar{position:relative}:host .pdf-viewer-toolbar__center{left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);z-index:1}:host .pdf-viewer-toolbar__start,:host .pdf-viewer-toolbar__end{position:relative;z-index:2}:host .pdf-viewer-sidebar__page.is-active .pdf-viewer-sidebar__thumb{outline:2px solid var(--ngs-color-primary)}:host .pdf-viewer-page__selection-layer{forced-color-adjust:none}\n"] }]
|
|
1999
|
+
}], ctorParameters: () => [], propDecorators: { viewerBody: [{ type: i0.ViewChild, args: ['viewerBody', { isSignal: true }] }], pageList: [{ type: i0.ViewChild, args: ['pageList', { isSignal: true }] }], annotationDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => PdfViewerAnnotationDef), { ...{ descendants: true }, isSignal: true }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], documentName: [{ type: i0.Input, args: [{ isSignal: true, alias: "documentName", required: false }] }], wasmUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "wasmUrl", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], scale: [{ type: i0.Input, args: [{ isSignal: true, alias: "scale", required: false }] }], minScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "minScale", required: false }] }], maxScale: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxScale", required: false }] }], zoomStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomStep", required: false }] }], maxRenderPixels: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRenderPixels", required: false }] }], maxRenderDimension: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRenderDimension", required: false }] }], renderAll: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderAll", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showPageList: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPageList", required: false }] }], showSearchPanel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearchPanel", required: false }] }], showAnnotationsPanel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAnnotationsPanel", required: false }] }], annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }], annotationsDataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotationsDataSource", required: false }] }], annotationTypeProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotationTypeProperty", required: false }] }], searchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchQuery", required: false }] }], withAnnotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "withAnnotations", required: false }] }], withForms: [{ type: i0.Input, args: [{ isSignal: true, alias: "withForms", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], pageChanged: [{ type: i0.Output, args: ["pageChanged"] }], pageRendered: [{ type: i0.Output, args: ["pageRendered"] }], error: [{ type: i0.Output, args: ["error"] }] } });
|
|
1137
2000
|
|
|
1138
2001
|
/**
|
|
1139
2002
|
* Generated bundle index. Do not edit.
|
|
1140
2003
|
*/
|
|
1141
2004
|
|
|
1142
|
-
export { PdfViewer, PdfViewerEngineService };
|
|
2005
|
+
export { PdfViewer, PdfViewerAnnotationDef, PdfViewerAnnotations, PdfViewerEngineService, PdfViewerSearch };
|
|
1143
2006
|
//# sourceMappingURL=ngstarter-ui-components-pdf-viewer.mjs.map
|