@ngstarter-ui/components 21.0.33 → 21.0.34
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 +53 -25
- 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-pdf-viewer.mjs +755 -25
- 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-pdf-viewer.d.ts +183 -4
|
@@ -1,13 +1,161 @@
|
|
|
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 } from '@angular/core';
|
|
4
|
+
import { 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, PanelAside, PanelSidebar } from '@ngstarter-ui/components/panel';
|
|
12
|
+
import { Toolbar, ToolbarSpacer, ToolbarTitle, ToolbarItem } from '@ngstarter-ui/components/toolbar';
|
|
13
|
+
import { isObservable } from 'rxjs';
|
|
14
|
+
import { Avatar } from '@ngstarter-ui/components/avatar';
|
|
15
|
+
import { Card, CardActions, CardAside, CardAvatar, CardContent, CardHeader, CardSubtitle, CardTitle } from '@ngstarter-ui/components/card';
|
|
16
|
+
import { Chip } from '@ngstarter-ui/components/chips';
|
|
17
|
+
import { FormField, IconButtonSuffix, IconPrefix } from '@ngstarter-ui/components/form-field';
|
|
18
|
+
import { Input } from '@ngstarter-ui/components/input';
|
|
10
19
|
import { PluginRegistry } from '@embedpdf/core';
|
|
20
|
+
import { Checkbox } from '@ngstarter-ui/components/checkbox';
|
|
21
|
+
|
|
22
|
+
class PdfViewerAnnotations {
|
|
23
|
+
annotations = input([], ...(ngDevMode ? [{ debugName: "annotations" }] : /* istanbul ignore next */ []));
|
|
24
|
+
annotationDefs = input([], ...(ngDevMode ? [{ debugName: "annotationDefs" }] : /* istanbul ignore next */ []));
|
|
25
|
+
annotationTypeProperty = input('type', ...(ngDevMode ? [{ debugName: "annotationTypeProperty" }] : /* istanbul ignore next */ []));
|
|
26
|
+
closed = output();
|
|
27
|
+
pageSelected = output();
|
|
28
|
+
filterQuery = signal('', ...(ngDevMode ? [{ debugName: "filterQuery" }] : /* istanbul ignore next */ []));
|
|
29
|
+
filteredAnnotations = computed(() => {
|
|
30
|
+
const query = this.filterQuery().trim().toLocaleLowerCase();
|
|
31
|
+
const annotations = this.annotations();
|
|
32
|
+
if (!query) {
|
|
33
|
+
return annotations;
|
|
34
|
+
}
|
|
35
|
+
return annotations.filter((annotation) => this.annotationMatchesFilter(annotation, query));
|
|
36
|
+
}, ...(ngDevMode ? [{ debugName: "filteredAnnotations" }] : /* istanbul ignore next */ []));
|
|
37
|
+
setFilterQuery(event) {
|
|
38
|
+
this.filterQuery.set(event.target.value);
|
|
39
|
+
}
|
|
40
|
+
clearFilterQuery() {
|
|
41
|
+
this.filterQuery.set('');
|
|
42
|
+
}
|
|
43
|
+
getAnnotationTemplate(annotation, index) {
|
|
44
|
+
const annotationDefs = this.annotationDefs();
|
|
45
|
+
const typeProperty = this.annotationTypeProperty();
|
|
46
|
+
const matchingDef = annotationDefs.find((def) => def.matches(annotation, index, typeProperty))
|
|
47
|
+
?? annotationDefs.find((def) => !def.hasWhen());
|
|
48
|
+
return matchingDef?.template ?? null;
|
|
49
|
+
}
|
|
50
|
+
getAnnotationTemplateContext(annotation, index) {
|
|
51
|
+
return {
|
|
52
|
+
$implicit: annotation,
|
|
53
|
+
annotation,
|
|
54
|
+
index,
|
|
55
|
+
goToPage: (pageNumber) => this.pageSelected.emit(pageNumber),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
getAnnotationTypeLabel(annotation) {
|
|
59
|
+
const label = annotation.label ?? annotation.type;
|
|
60
|
+
return typeof label === 'string' && label.trim().length > 0 ? label : 'Comment';
|
|
61
|
+
}
|
|
62
|
+
getAvatarLabel(annotation) {
|
|
63
|
+
const explicitLabel = annotation.avatarLabel;
|
|
64
|
+
if (typeof explicitLabel === 'string' && explicitLabel.trim().length > 0) {
|
|
65
|
+
return explicitLabel.trim();
|
|
66
|
+
}
|
|
67
|
+
return annotation.author
|
|
68
|
+
.split(/\s+/)
|
|
69
|
+
.filter(Boolean)
|
|
70
|
+
.slice(0, 2)
|
|
71
|
+
.map((part) => part[0]?.toUpperCase())
|
|
72
|
+
.join('');
|
|
73
|
+
}
|
|
74
|
+
getAvatarImage(annotation) {
|
|
75
|
+
return typeof annotation.avatarUrl === 'string' ? annotation.avatarUrl : '';
|
|
76
|
+
}
|
|
77
|
+
getReplyLabel(annotation) {
|
|
78
|
+
return typeof annotation.replyLabel === 'string' && annotation.replyLabel.trim().length > 0
|
|
79
|
+
? annotation.replyLabel
|
|
80
|
+
: 'Reply';
|
|
81
|
+
}
|
|
82
|
+
annotationMatchesFilter(annotation, query) {
|
|
83
|
+
return [
|
|
84
|
+
annotation.author,
|
|
85
|
+
annotation.time,
|
|
86
|
+
annotation.text,
|
|
87
|
+
annotation.type,
|
|
88
|
+
annotation.label,
|
|
89
|
+
`page ${annotation.pageNumber}`,
|
|
90
|
+
]
|
|
91
|
+
.filter((value) => typeof value === 'string')
|
|
92
|
+
.some((value) => value.toLocaleLowerCase().includes(query));
|
|
93
|
+
}
|
|
94
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotations, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
95
|
+
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\" aria-label=\"PDF annotations\">\n <ngs-panel-header class=\"pdf-viewer-annotations__toolbar\">\n <h3>Annotations</h3>\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-panel-header>\n\n <ngs-panel-content>\n <div class=\"pdf-viewer-annotations__content\">\n <div class=\"pdf-viewer-annotations__filters\">\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\n <button\n ngsIconButton\n type=\"button\"\n aria-label=\"Annotation filters\">\n <ngs-icon name=\"fluent:filter-24-regular\" />\n </button>\n </div>\n\n <div class=\"pdf-viewer-annotations__list\">\n <ng-template #defaultAnnotationTemplate let-annotation let-goToPage=\"goToPage\">\n <ngs-card class=\"pdf-viewer-annotation-card\">\n <ngs-card-header>\n <ngs-avatar\n ngsCardAvatar\n variant=\"tonal\"\n [image]=\"getAvatarImage(annotation)\"\n [label]=\"getAvatarLabel(annotation)\"\n [alt]=\"annotation.author\" />\n\n <ngs-card-title>{{ annotation.author }}</ngs-card-title>\n @if (annotation.time) {\n <ngs-card-subtitle>{{ annotation.time }}</ngs-card-subtitle>\n }\n\n <ngs-card-aside>\n <ngs-chip appearance=\"tonal\">{{ getAnnotationTypeLabel(annotation) }}</ngs-chip>\n </ngs-card-aside>\n </ngs-card-header>\n\n <ngs-card-content>\n <p>{{ annotation.text }}</p>\n </ngs-card-content>\n\n <ngs-card-actions align=\"between\">\n <button ngsButton=\"tonal\" type=\"button\" (click)=\"goToPage(annotation.pageNumber)\">\n Page {{ annotation.pageNumber }}\n </button>\n\n <button ngsButton=\"text\" type=\"button\">\n <ngs-icon name=\"fluent:arrow-reply-24-regular\" />\n {{ getReplyLabel(annotation) }}\n </button>\n </ngs-card-actions>\n </ngs-card>\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=\"pdf-viewer-annotations__empty\">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%}:host ngs-panel.pdf-viewer-annotations{background:var(--ngs-color-surface)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__toolbar{display:flex;align-items:center;justify-content:space-between;padding:0 calc(var(--spacing, .25rem) * 4);border-bottom:1px solid var(--ngs-color-border)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__toolbar h3{margin:0;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600;letter-spacing:0;text-transform:uppercase}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__content{display:flex;flex-direction:column;min-height:100%;background:var(--ngs-color-surface-container-lowest)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__filters{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding:calc(var(--spacing, .25rem) * 3) calc(var(--spacing, .25rem) * 4);border-bottom:1px solid var(--ngs-color-border)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__list{display:flex;flex:1 1 auto;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);padding:calc(var(--spacing, .25rem) * 4)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__empty{padding:calc(var(--spacing, .25rem) * 4);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);text-align:center}:host ngs-card.pdf-viewer-annotation-card{--ngs-card-bg: var(--ngs-color-surface);--ngs-card-radius: var(--ngs-radius-md);--ngs-card-padding: calc(var(--spacing, .25rem) * 4)}:host ngs-card.pdf-viewer-annotation-card ngs-card-header{align-items:flex-start}:host ngs-card.pdf-viewer-annotation-card ngs-card-title{font-weight:600}:host ngs-card.pdf-viewer-annotation-card ngs-card-subtitle{color:var(--ngs-color-on-surface-variant)}:host ngs-card.pdf-viewer-annotation-card ngs-card-content p{margin:0;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);line-height:1.45}:host ngs-card.pdf-viewer-annotation-card ngs-card-actions{padding:0 calc(var(--spacing, .25rem) * 3) calc(var(--spacing, .25rem) * 3)}:host ngs-card.pdf-viewer-annotation-card ngs-chip{text-transform:uppercase}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: Avatar, selector: "ngs-avatar,[ngs-avatar]", inputs: ["image", "variant", "clickable", "label", "alt", "automaticColor", "presenceIndicator"], exportAs: ["ngsAvatar"] }, { 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: Card, selector: "ngs-card", inputs: ["appearance"], exportAs: ["ngsCard"] }, { kind: "component", type: CardActions, selector: "ngs-card-actions, [ngs-card-actions], [ngsCardActions]", inputs: ["align"] }, { kind: "component", type: CardAside, selector: "ngs-card-aside, [ngs-card-aside], ngsCardAside" }, { kind: "directive", type: CardAvatar, selector: "ngs-card-avatar, [ngs-card-avatar], [ngsCardAvatar]" }, { kind: "component", type: CardContent, selector: "ngs-card-content, [ngs-card-content], [ngsCardContent]", inputs: ["withoutPadding"], exportAs: ["ngsCardContent"] }, { kind: "component", type: CardHeader, selector: "ngs-card-header", exportAs: ["ngsCardHeader"] }, { kind: "component", type: CardSubtitle, selector: "ngs-card-subtitle, [ngs-card-subtitle], [ngsCardSubtitle]", exportAs: ["ngsCardSubtitle"] }, { kind: "component", type: CardTitle, selector: "ngs-card-title, [ngs-card-title], [ngsCardTitle]", exportAs: ["ngsCardTitle"] }, { kind: "component", type: Chip, selector: "ngs-chip", inputs: ["appearance", "disabled", "value"], outputs: ["destroyed", "removed"], exportAs: ["ngsChip"] }, { 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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
96
|
+
}
|
|
97
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotations, decorators: [{
|
|
98
|
+
type: Component,
|
|
99
|
+
args: [{ selector: 'ngs-pdf-viewer-annotations', standalone: true, imports: [
|
|
100
|
+
Avatar,
|
|
101
|
+
Button,
|
|
102
|
+
Card,
|
|
103
|
+
CardActions,
|
|
104
|
+
CardAside,
|
|
105
|
+
CardAvatar,
|
|
106
|
+
CardContent,
|
|
107
|
+
CardHeader,
|
|
108
|
+
CardSubtitle,
|
|
109
|
+
CardTitle,
|
|
110
|
+
Chip,
|
|
111
|
+
FormField,
|
|
112
|
+
Icon,
|
|
113
|
+
IconButtonSuffix,
|
|
114
|
+
IconPrefix,
|
|
115
|
+
Input,
|
|
116
|
+
NgTemplateOutlet,
|
|
117
|
+
Panel,
|
|
118
|
+
PanelContent,
|
|
119
|
+
PanelHeader,
|
|
120
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
121
|
+
class: 'ngs-pdf-viewer-annotations',
|
|
122
|
+
}, template: "<ngs-panel class=\"pdf-viewer-annotations\" aria-label=\"PDF annotations\">\n <ngs-panel-header class=\"pdf-viewer-annotations__toolbar\">\n <h3>Annotations</h3>\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-panel-header>\n\n <ngs-panel-content>\n <div class=\"pdf-viewer-annotations__content\">\n <div class=\"pdf-viewer-annotations__filters\">\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\n <button\n ngsIconButton\n type=\"button\"\n aria-label=\"Annotation filters\">\n <ngs-icon name=\"fluent:filter-24-regular\" />\n </button>\n </div>\n\n <div class=\"pdf-viewer-annotations__list\">\n <ng-template #defaultAnnotationTemplate let-annotation let-goToPage=\"goToPage\">\n <ngs-card class=\"pdf-viewer-annotation-card\">\n <ngs-card-header>\n <ngs-avatar\n ngsCardAvatar\n variant=\"tonal\"\n [image]=\"getAvatarImage(annotation)\"\n [label]=\"getAvatarLabel(annotation)\"\n [alt]=\"annotation.author\" />\n\n <ngs-card-title>{{ annotation.author }}</ngs-card-title>\n @if (annotation.time) {\n <ngs-card-subtitle>{{ annotation.time }}</ngs-card-subtitle>\n }\n\n <ngs-card-aside>\n <ngs-chip appearance=\"tonal\">{{ getAnnotationTypeLabel(annotation) }}</ngs-chip>\n </ngs-card-aside>\n </ngs-card-header>\n\n <ngs-card-content>\n <p>{{ annotation.text }}</p>\n </ngs-card-content>\n\n <ngs-card-actions align=\"between\">\n <button ngsButton=\"tonal\" type=\"button\" (click)=\"goToPage(annotation.pageNumber)\">\n Page {{ annotation.pageNumber }}\n </button>\n\n <button ngsButton=\"text\" type=\"button\">\n <ngs-icon name=\"fluent:arrow-reply-24-regular\" />\n {{ getReplyLabel(annotation) }}\n </button>\n </ngs-card-actions>\n </ngs-card>\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=\"pdf-viewer-annotations__empty\">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%}:host ngs-panel.pdf-viewer-annotations{background:var(--ngs-color-surface)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__toolbar{display:flex;align-items:center;justify-content:space-between;padding:0 calc(var(--spacing, .25rem) * 4);border-bottom:1px solid var(--ngs-color-border)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__toolbar h3{margin:0;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-sm);font-weight:600;letter-spacing:0;text-transform:uppercase}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__content{display:flex;flex-direction:column;min-height:100%;background:var(--ngs-color-surface-container-lowest)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__filters{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:calc(var(--spacing, .25rem) * 2);padding:calc(var(--spacing, .25rem) * 3) calc(var(--spacing, .25rem) * 4);border-bottom:1px solid var(--ngs-color-border)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__list{display:flex;flex:1 1 auto;flex-direction:column;gap:calc(var(--spacing, .25rem) * 4);padding:calc(var(--spacing, .25rem) * 4)}:host ngs-panel.pdf-viewer-annotations .pdf-viewer-annotations__empty{padding:calc(var(--spacing, .25rem) * 4);color:var(--ngs-color-on-surface-variant);font-size:var(--ngs-font-size-sm);text-align:center}:host ngs-card.pdf-viewer-annotation-card{--ngs-card-bg: var(--ngs-color-surface);--ngs-card-radius: var(--ngs-radius-md);--ngs-card-padding: calc(var(--spacing, .25rem) * 4)}:host ngs-card.pdf-viewer-annotation-card ngs-card-header{align-items:flex-start}:host ngs-card.pdf-viewer-annotation-card ngs-card-title{font-weight:600}:host ngs-card.pdf-viewer-annotation-card ngs-card-subtitle{color:var(--ngs-color-on-surface-variant)}:host ngs-card.pdf-viewer-annotation-card ngs-card-content p{margin:0;color:var(--ngs-color-on-surface);font-size:var(--ngs-font-size-base);line-height:1.45}:host ngs-card.pdf-viewer-annotation-card ngs-card-actions{padding:0 calc(var(--spacing, .25rem) * 3) calc(var(--spacing, .25rem) * 3)}:host ngs-card.pdf-viewer-annotation-card ngs-chip{text-transform:uppercase}\n/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */\n"] }]
|
|
123
|
+
}], 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"] }] } });
|
|
124
|
+
|
|
125
|
+
class PdfViewerAnnotationDef {
|
|
126
|
+
template;
|
|
127
|
+
annotationWhen = input(undefined, { ...(ngDevMode ? { debugName: "annotationWhen" } : /* istanbul ignore next */ {}), alias: 'ngsPdfViewerAnnotation' });
|
|
128
|
+
defWhen = input(undefined, { ...(ngDevMode ? { debugName: "defWhen" } : /* istanbul ignore next */ {}), alias: 'ngsPdfViewerAnnotationDef' });
|
|
129
|
+
when = input(undefined, { ...(ngDevMode ? { debugName: "when" } : /* istanbul ignore next */ {}), alias: 'ngsPdfViewerAnnotationWhen' });
|
|
130
|
+
constructor(template) {
|
|
131
|
+
this.template = template;
|
|
132
|
+
}
|
|
133
|
+
matches(annotation, index, typeProperty) {
|
|
134
|
+
const whenValue = this.whenValue();
|
|
135
|
+
if (!whenValue) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
if (typeof whenValue === 'function') {
|
|
139
|
+
return whenValue(annotation, index);
|
|
140
|
+
}
|
|
141
|
+
return annotation[typeProperty] === whenValue;
|
|
142
|
+
}
|
|
143
|
+
hasWhen() {
|
|
144
|
+
return !!this.whenValue();
|
|
145
|
+
}
|
|
146
|
+
whenValue() {
|
|
147
|
+
return this.when() ?? this.defWhen() ?? this.annotationWhen();
|
|
148
|
+
}
|
|
149
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotationDef, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
150
|
+
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 });
|
|
151
|
+
}
|
|
152
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerAnnotationDef, decorators: [{
|
|
153
|
+
type: Directive,
|
|
154
|
+
args: [{
|
|
155
|
+
selector: '[ngsPdfViewerAnnotationDef], [ngsPdfViewerAnnotation]',
|
|
156
|
+
standalone: true,
|
|
157
|
+
}]
|
|
158
|
+
}], 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
159
|
|
|
12
160
|
class PdfViewerEngineService {
|
|
13
161
|
engines = new Map();
|
|
@@ -38,6 +186,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
38
186
|
}]
|
|
39
187
|
}] });
|
|
40
188
|
|
|
189
|
+
class PdfViewerSearch {
|
|
190
|
+
results = input([], ...(ngDevMode ? [{ debugName: "results" }] : /* istanbul ignore next */ []));
|
|
191
|
+
query = input('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
192
|
+
closed = output();
|
|
193
|
+
resultSelected = output();
|
|
194
|
+
searchChanged = output();
|
|
195
|
+
caseSensitive = signal(false, ...(ngDevMode ? [{ debugName: "caseSensitive" }] : /* istanbul ignore next */ []));
|
|
196
|
+
wholeWord = signal(false, ...(ngDevMode ? [{ debugName: "wholeWord" }] : /* istanbul ignore next */ []));
|
|
197
|
+
activeResultIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeResultIndex" }] : /* istanbul ignore next */ []));
|
|
198
|
+
queryValue = signal('', ...(ngDevMode ? [{ debugName: "queryValue" }] : /* istanbul ignore next */ []));
|
|
199
|
+
hasQuery = computed(() => this.queryValue().trim().length > 0, ...(ngDevMode ? [{ debugName: "hasQuery" }] : /* istanbul ignore next */ []));
|
|
200
|
+
visibleResultGroups = computed(() => {
|
|
201
|
+
const groups = new Map();
|
|
202
|
+
this.results().forEach((result, index) => {
|
|
203
|
+
const group = groups.get(result.pageNumber) ?? {
|
|
204
|
+
pageNumber: result.pageNumber,
|
|
205
|
+
results: [],
|
|
206
|
+
};
|
|
207
|
+
group.results.push({ result, index });
|
|
208
|
+
groups.set(result.pageNumber, group);
|
|
209
|
+
});
|
|
210
|
+
return [...groups.values()];
|
|
211
|
+
}, ...(ngDevMode ? [{ debugName: "visibleResultGroups" }] : /* istanbul ignore next */ []));
|
|
212
|
+
resultCountLabel = computed(() => {
|
|
213
|
+
const count = this.results().length;
|
|
214
|
+
return `${count} ${count === 1 ? 'result' : 'results'} found`;
|
|
215
|
+
}, ...(ngDevMode ? [{ debugName: "resultCountLabel" }] : /* istanbul ignore next */ []));
|
|
216
|
+
constructor() {
|
|
217
|
+
effect(() => {
|
|
218
|
+
const query = this.query();
|
|
219
|
+
untracked(() => {
|
|
220
|
+
this.queryValue.set(query);
|
|
221
|
+
this.activeResultIndex.set(0);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
setQuery(event) {
|
|
226
|
+
this.queryValue.set(event.target.value);
|
|
227
|
+
this.activeResultIndex.set(0);
|
|
228
|
+
this.emitSearchChanged();
|
|
229
|
+
}
|
|
230
|
+
clearQuery() {
|
|
231
|
+
this.queryValue.set('');
|
|
232
|
+
this.activeResultIndex.set(0);
|
|
233
|
+
this.emitSearchChanged();
|
|
234
|
+
}
|
|
235
|
+
setCaseSensitive(value) {
|
|
236
|
+
this.caseSensitive.set(value);
|
|
237
|
+
this.activeResultIndex.set(0);
|
|
238
|
+
this.emitSearchChanged();
|
|
239
|
+
}
|
|
240
|
+
setWholeWord(value) {
|
|
241
|
+
this.wholeWord.set(value);
|
|
242
|
+
this.activeResultIndex.set(0);
|
|
243
|
+
this.emitSearchChanged();
|
|
244
|
+
}
|
|
245
|
+
previousResult() {
|
|
246
|
+
const count = this.results().length;
|
|
247
|
+
if (count === 0) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this.activeResultIndex.update((index) => (index - 1 + count) % count);
|
|
251
|
+
}
|
|
252
|
+
nextResult() {
|
|
253
|
+
const count = this.results().length;
|
|
254
|
+
if (count === 0) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
this.activeResultIndex.update((index) => (index + 1) % count);
|
|
258
|
+
}
|
|
259
|
+
selectResult(result, index) {
|
|
260
|
+
this.activeResultIndex.set(index);
|
|
261
|
+
this.resultSelected.emit(result);
|
|
262
|
+
}
|
|
263
|
+
resultParts(result) {
|
|
264
|
+
const query = this.queryValue().trim();
|
|
265
|
+
if (!query) {
|
|
266
|
+
return [{ text: result.excerpt, highlight: false }];
|
|
267
|
+
}
|
|
268
|
+
const flags = this.caseSensitive() ? 'g' : 'gi';
|
|
269
|
+
const escapedQuery = this.escapeRegExp(query);
|
|
270
|
+
const pattern = this.wholeWord()
|
|
271
|
+
? new RegExp(`\\b${escapedQuery}\\b`, flags)
|
|
272
|
+
: new RegExp(escapedQuery, flags);
|
|
273
|
+
const parts = [];
|
|
274
|
+
let lastIndex = 0;
|
|
275
|
+
let match;
|
|
276
|
+
while ((match = pattern.exec(result.excerpt)) !== null) {
|
|
277
|
+
if (match.index > lastIndex) {
|
|
278
|
+
parts.push({ text: result.excerpt.slice(lastIndex, match.index), highlight: false });
|
|
279
|
+
}
|
|
280
|
+
parts.push({ text: match[0], highlight: true });
|
|
281
|
+
lastIndex = match.index + match[0].length;
|
|
282
|
+
if (match[0].length === 0) {
|
|
283
|
+
pattern.lastIndex++;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (lastIndex < result.excerpt.length) {
|
|
287
|
+
parts.push({ text: result.excerpt.slice(lastIndex), highlight: false });
|
|
288
|
+
}
|
|
289
|
+
return parts.length > 0 ? parts : [{ text: result.excerpt, highlight: false }];
|
|
290
|
+
}
|
|
291
|
+
emitSearchChanged() {
|
|
292
|
+
this.searchChanged.emit({
|
|
293
|
+
query: this.queryValue(),
|
|
294
|
+
options: {
|
|
295
|
+
caseSensitive: this.caseSensitive(),
|
|
296
|
+
wholeWord: this.wholeWord(),
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
escapeRegExp(value) {
|
|
301
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
302
|
+
}
|
|
303
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
304
|
+
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-5 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-content>\n <div class=\"pdf-viewer-search__content flex min-h-full flex-col gap-5 p-6\">\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 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 <ngs-divider />\n\n <div class=\"pdf-viewer-search__summary 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 <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\">\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 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: Divider, selector: "ngs-divider", inputs: ["vertical", "inset", "fixedHeight"], exportAs: ["ngsDivider"] }, { 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" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
305
|
+
}
|
|
306
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewerSearch, decorators: [{
|
|
307
|
+
type: Component,
|
|
308
|
+
args: [{ selector: 'ngs-pdf-viewer-search', standalone: true, imports: [
|
|
309
|
+
Button,
|
|
310
|
+
Checkbox,
|
|
311
|
+
Divider,
|
|
312
|
+
FormField,
|
|
313
|
+
Icon,
|
|
314
|
+
IconButtonSuffix,
|
|
315
|
+
IconPrefix,
|
|
316
|
+
Input,
|
|
317
|
+
Panel,
|
|
318
|
+
PanelContent,
|
|
319
|
+
PanelHeader,
|
|
320
|
+
Toolbar,
|
|
321
|
+
ToolbarSpacer,
|
|
322
|
+
ToolbarTitle,
|
|
323
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
324
|
+
class: 'ngs-pdf-viewer-search',
|
|
325
|
+
}, template: "<ngs-panel class=\"pdf-viewer-search\" aria-label=\"PDF search\">\n <ngs-panel-header class=\"ps-5 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-content>\n <div class=\"pdf-viewer-search__content flex min-h-full flex-col gap-5 p-6\">\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 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 <ngs-divider />\n\n <div class=\"pdf-viewer-search__summary 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 <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\">\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 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"] }]
|
|
326
|
+
}], 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"] }] } });
|
|
327
|
+
|
|
41
328
|
class PdfViewer {
|
|
42
329
|
textSelectionHorizontalPadding = 2;
|
|
43
330
|
textSelectionLineHeight = 1.2;
|
|
@@ -49,16 +336,26 @@ class PdfViewer {
|
|
|
49
336
|
isBrowser = isPlatformBrowser(this.platformId);
|
|
50
337
|
viewerBody = viewChild('viewerBody', ...(ngDevMode ? [{ debugName: "viewerBody" }] : /* istanbul ignore next */ []));
|
|
51
338
|
pageList = viewChild('pageList', ...(ngDevMode ? [{ debugName: "pageList" }] : /* istanbul ignore next */ []));
|
|
339
|
+
annotationDefs = contentChildren(PdfViewerAnnotationDef, { ...(ngDevMode ? { debugName: "annotationDefs" } : /* istanbul ignore next */ {}), descendants: true });
|
|
52
340
|
src = input(null, ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
|
|
341
|
+
documentName = input(null, ...(ngDevMode ? [{ debugName: "documentName" }] : /* istanbul ignore next */ []));
|
|
53
342
|
wasmUrl = input('/assets/embedpdf/pdfium.wasm', ...(ngDevMode ? [{ debugName: "wasmUrl" }] : /* istanbul ignore next */ []));
|
|
54
343
|
page = input(1, { ...(ngDevMode ? { debugName: "page" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
55
344
|
scale = input(1, { ...(ngDevMode ? { debugName: "scale" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
56
345
|
minScale = input(0.2, { ...(ngDevMode ? { debugName: "minScale" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
57
346
|
maxScale = input(60, { ...(ngDevMode ? { debugName: "maxScale" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
58
347
|
zoomStep = input(0.1, { ...(ngDevMode ? { debugName: "zoomStep" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
348
|
+
maxRenderPixels = input(128_000_000, { ...(ngDevMode ? { debugName: "maxRenderPixels" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
349
|
+
maxRenderDimension = input(12_000, { ...(ngDevMode ? { debugName: "maxRenderDimension" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
59
350
|
renderAll = input(true, { ...(ngDevMode ? { debugName: "renderAll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
60
351
|
showToolbar = input(true, { ...(ngDevMode ? { debugName: "showToolbar" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
61
352
|
showPageList = input(true, { ...(ngDevMode ? { debugName: "showPageList" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
353
|
+
showSearchPanel = input(true, { ...(ngDevMode ? { debugName: "showSearchPanel" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
354
|
+
showAnnotationsPanel = input(false, { ...(ngDevMode ? { debugName: "showAnnotationsPanel" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
355
|
+
annotations = input([], ...(ngDevMode ? [{ debugName: "annotations" }] : /* istanbul ignore next */ []));
|
|
356
|
+
annotationsDataSource = input(null, ...(ngDevMode ? [{ debugName: "annotationsDataSource" }] : /* istanbul ignore next */ []));
|
|
357
|
+
annotationTypeProperty = input('type', ...(ngDevMode ? [{ debugName: "annotationTypeProperty" }] : /* istanbul ignore next */ []));
|
|
358
|
+
searchQuery = input('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
|
|
62
359
|
withAnnotations = input(true, { ...(ngDevMode ? { debugName: "withAnnotations" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
63
360
|
withForms = input(true, { ...(ngDevMode ? { debugName: "withForms" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
64
361
|
loaded = output();
|
|
@@ -72,12 +369,20 @@ class PdfViewer {
|
|
|
72
369
|
pageCount = signal(0, ...(ngDevMode ? [{ debugName: "pageCount" }] : /* istanbul ignore next */ []));
|
|
73
370
|
activePage = signal(1, ...(ngDevMode ? [{ debugName: "activePage" }] : /* istanbul ignore next */ []));
|
|
74
371
|
zoom = signal(1, ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
75
|
-
pageListVisible = signal(
|
|
372
|
+
pageListVisible = signal(false, ...(ngDevMode ? [{ debugName: "pageListVisible" }] : /* istanbul ignore next */ []));
|
|
373
|
+
searchPanelVisible = signal(false, ...(ngDevMode ? [{ debugName: "searchPanelVisible" }] : /* istanbul ignore next */ []));
|
|
374
|
+
annotationsPanelVisible = signal(false, ...(ngDevMode ? [{ debugName: "annotationsPanelVisible" }] : /* istanbul ignore next */ []));
|
|
76
375
|
spreadMode = signal('single', ...(ngDevMode ? [{ debugName: "spreadMode" }] : /* istanbul ignore next */ []));
|
|
77
376
|
scrollLayout = signal('horizontal', ...(ngDevMode ? [{ debugName: "scrollLayout" }] : /* istanbul ignore next */ []));
|
|
377
|
+
zoomMode = signal('custom', ...(ngDevMode ? [{ debugName: "zoomMode" }] : /* istanbul ignore next */ []));
|
|
378
|
+
annotationItems = signal([], ...(ngDevMode ? [{ debugName: "annotationItems" }] : /* istanbul ignore next */ []));
|
|
379
|
+
activeSearchQuery = signal('', ...(ngDevMode ? [{ debugName: "activeSearchQuery" }] : /* istanbul ignore next */ []));
|
|
380
|
+
pdfSearchResults = signal([], ...(ngDevMode ? [{ debugName: "pdfSearchResults" }] : /* istanbul ignore next */ []));
|
|
78
381
|
selectionRects = signal([], ...(ngDevMode ? [{ debugName: "selectionRects" }] : /* istanbul ignore next */ []));
|
|
79
382
|
hasDocument = computed(() => this.pageCount() > 0, ...(ngDevMode ? [{ debugName: "hasDocument" }] : /* istanbul ignore next */ []));
|
|
80
|
-
isPageListVisible = computed(() => this.showPageList() && this.
|
|
383
|
+
isPageListVisible = computed(() => this.showPageList() && this.pageListVisible(), ...(ngDevMode ? [{ debugName: "isPageListVisible" }] : /* istanbul ignore next */ []));
|
|
384
|
+
isSearchPanelVisible = computed(() => this.showSearchPanel() && this.searchPanelVisible(), ...(ngDevMode ? [{ debugName: "isSearchPanelVisible" }] : /* istanbul ignore next */ []));
|
|
385
|
+
isAnnotationsPanelVisible = computed(() => this.showAnnotationsPanel() && !this.searchPanelVisible() && this.annotationsPanelVisible(), ...(ngDevMode ? [{ debugName: "isAnnotationsPanelVisible" }] : /* istanbul ignore next */ []));
|
|
81
386
|
thumbnailPageMap = computed(() => new Map(this.thumbnailPages().map((thumbnail) => [thumbnail.pageNumber, thumbnail])), ...(ngDevMode ? [{ debugName: "thumbnailPageMap" }] : /* istanbul ignore next */ []));
|
|
82
387
|
pageItems = computed(() => Array.from({ length: this.pageCount() }, (_, index) => {
|
|
83
388
|
const pageNumber = index + 1;
|
|
@@ -91,6 +396,8 @@ class PdfViewer {
|
|
|
91
396
|
canZoomOut = computed(() => this.zoom() > this.getScaleBounds().min, ...(ngDevMode ? [{ debugName: "canZoomOut" }] : /* istanbul ignore next */ []));
|
|
92
397
|
canZoomIn = computed(() => this.zoom() < this.getScaleBounds().max, ...(ngDevMode ? [{ debugName: "canZoomIn" }] : /* istanbul ignore next */ []));
|
|
93
398
|
zoomLabel = computed(() => `${Math.round(this.zoom() * 100)}%`, ...(ngDevMode ? [{ debugName: "zoomLabel" }] : /* istanbul ignore next */ []));
|
|
399
|
+
displayDocumentName = computed(() => this.documentName() || this.getSourceName(this.src()), ...(ngDevMode ? [{ debugName: "displayDocumentName" }] : /* istanbul ignore next */ []));
|
|
400
|
+
zoomPresets = [0.25, 0.5, 1, 1.25, 1.5, 2, 4, 8, 16];
|
|
94
401
|
engine = null;
|
|
95
402
|
pdfDocument = null;
|
|
96
403
|
registry = null;
|
|
@@ -98,21 +405,30 @@ class PdfViewer {
|
|
|
98
405
|
visiblePageRatios = new Map();
|
|
99
406
|
loadToken = 0;
|
|
100
407
|
renderToken = 0;
|
|
408
|
+
searchToken = 0;
|
|
101
409
|
scrollSyncFrame = null;
|
|
102
410
|
programmaticScrollTargetPage = null;
|
|
103
411
|
programmaticScrollTimeout = null;
|
|
104
412
|
pageObserverFrame = null;
|
|
105
413
|
pageObserverTimeout = null;
|
|
106
414
|
visiblePageRenderFrame = null;
|
|
415
|
+
visiblePageRenderTimeout = null;
|
|
107
416
|
pageObserverRefreshAttempts = 0;
|
|
108
417
|
selectionStart = null;
|
|
109
418
|
isViewInitialized = false;
|
|
419
|
+
annotationDataSourceToken = 0;
|
|
420
|
+
annotationDataSourceCleanup = null;
|
|
421
|
+
lastZoomChangeTime = 0;
|
|
110
422
|
programmaticScrollMinDuration = 900;
|
|
111
423
|
programmaticScrollMaxDuration = 6000;
|
|
424
|
+
qualityRenderZoomIdleDelay = 160;
|
|
112
425
|
constructor() {
|
|
113
426
|
this.destroyRef.onDestroy(() => {
|
|
114
427
|
this.loadToken++;
|
|
115
428
|
this.renderToken++;
|
|
429
|
+
this.searchToken++;
|
|
430
|
+
this.annotationDataSourceToken++;
|
|
431
|
+
this.annotationDataSourceCleanup?.();
|
|
116
432
|
this.cancelScrollSyncFrame();
|
|
117
433
|
this.clearProgrammaticScrollLock();
|
|
118
434
|
this.cancelPageObserverFrame();
|
|
@@ -123,6 +439,25 @@ class PdfViewer {
|
|
|
123
439
|
void this.closeDocument();
|
|
124
440
|
void this.registry?.destroy();
|
|
125
441
|
});
|
|
442
|
+
effect((onCleanup) => {
|
|
443
|
+
const dataSource = this.annotationsDataSource();
|
|
444
|
+
const fallbackAnnotations = this.annotations();
|
|
445
|
+
const source = this.src();
|
|
446
|
+
const documentName = this.documentName() || this.getSourceName(source);
|
|
447
|
+
const pageCount = this.pageCount();
|
|
448
|
+
const token = ++this.annotationDataSourceToken;
|
|
449
|
+
const context = {
|
|
450
|
+
source,
|
|
451
|
+
documentName,
|
|
452
|
+
pageCount,
|
|
453
|
+
};
|
|
454
|
+
this.annotationDataSourceCleanup?.();
|
|
455
|
+
this.annotationDataSourceCleanup = untracked(() => this.loadAnnotationsDataSource(dataSource ?? fallbackAnnotations, context, token));
|
|
456
|
+
onCleanup(() => {
|
|
457
|
+
this.annotationDataSourceCleanup?.();
|
|
458
|
+
this.annotationDataSourceCleanup = null;
|
|
459
|
+
});
|
|
460
|
+
});
|
|
126
461
|
effect(() => {
|
|
127
462
|
const source = this.src();
|
|
128
463
|
const wasmUrl = this.wasmUrl();
|
|
@@ -145,6 +480,18 @@ class PdfViewer {
|
|
|
145
480
|
this.activePage.set(requestedPage);
|
|
146
481
|
}
|
|
147
482
|
});
|
|
483
|
+
effect(() => {
|
|
484
|
+
const query = this.searchQuery();
|
|
485
|
+
if (query !== untracked(() => this.activeSearchQuery())) {
|
|
486
|
+
this.activeSearchQuery.set(query);
|
|
487
|
+
untracked(() => {
|
|
488
|
+
void this.searchPdf(query, {
|
|
489
|
+
caseSensitive: false,
|
|
490
|
+
wholeWord: false,
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
});
|
|
148
495
|
effect(() => {
|
|
149
496
|
const zoom = this.zoom();
|
|
150
497
|
const renderAll = this.renderAll();
|
|
@@ -190,14 +537,70 @@ class PdfViewer {
|
|
|
190
537
|
this.setPage(this.activePage() + 1);
|
|
191
538
|
}
|
|
192
539
|
zoomIn() {
|
|
540
|
+
this.zoomMode.set('custom');
|
|
193
541
|
this.setZoom(this.roundZoom(this.zoom() + this.sanitizeZoomStep()));
|
|
194
542
|
}
|
|
195
543
|
zoomOut() {
|
|
544
|
+
this.zoomMode.set('custom');
|
|
196
545
|
this.setZoom(this.roundZoom(this.zoom() - this.sanitizeZoomStep()));
|
|
197
546
|
}
|
|
198
547
|
togglePageList() {
|
|
199
548
|
this.pageListVisible.update((isVisible) => !isVisible);
|
|
200
549
|
}
|
|
550
|
+
toggleSearchPanel() {
|
|
551
|
+
const nextVisible = !this.searchPanelVisible();
|
|
552
|
+
this.searchPanelVisible.set(nextVisible);
|
|
553
|
+
if (nextVisible) {
|
|
554
|
+
this.annotationsPanelVisible.set(false);
|
|
555
|
+
const query = this.searchQuery();
|
|
556
|
+
this.activeSearchQuery.set(query);
|
|
557
|
+
void this.searchPdf(query, {
|
|
558
|
+
caseSensitive: false,
|
|
559
|
+
wholeWord: false,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
toggleAnnotationsPanel() {
|
|
564
|
+
const nextVisible = !this.annotationsPanelVisible();
|
|
565
|
+
this.annotationsPanelVisible.set(nextVisible);
|
|
566
|
+
if (nextVisible) {
|
|
567
|
+
this.searchPanelVisible.set(false);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
closeAsidePanel() {
|
|
571
|
+
this.searchPanelVisible.set(false);
|
|
572
|
+
this.annotationsPanelVisible.set(false);
|
|
573
|
+
}
|
|
574
|
+
updatePdfSearch(event) {
|
|
575
|
+
this.activeSearchQuery.set(event.query);
|
|
576
|
+
return this.searchPdf(event.query, event.options);
|
|
577
|
+
}
|
|
578
|
+
selectSearchResult(result) {
|
|
579
|
+
this.setPage(result.pageNumber);
|
|
580
|
+
}
|
|
581
|
+
setZoomPreset(scale) {
|
|
582
|
+
this.zoomMode.set('custom');
|
|
583
|
+
this.setZoom(scale);
|
|
584
|
+
}
|
|
585
|
+
isZoomPresetSelected(scale) {
|
|
586
|
+
return this.zoomMode() === 'custom' && Math.abs(this.zoom() - this.sanitizeScale(scale)) < 0.0001;
|
|
587
|
+
}
|
|
588
|
+
fitToPage() {
|
|
589
|
+
const scale = this.getFitScale('fit-page');
|
|
590
|
+
if (scale === null) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
this.zoomMode.set('fit-page');
|
|
594
|
+
this.setZoom(scale);
|
|
595
|
+
}
|
|
596
|
+
fitToWidth() {
|
|
597
|
+
const scale = this.getFitScale('fit-width');
|
|
598
|
+
if (scale === null) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
this.zoomMode.set('fit-width');
|
|
602
|
+
this.setZoom(scale);
|
|
603
|
+
}
|
|
201
604
|
setSpreadMode(mode) {
|
|
202
605
|
this.spreadMode.set(mode);
|
|
203
606
|
}
|
|
@@ -252,12 +655,31 @@ class PdfViewer {
|
|
|
252
655
|
this.scheduleCurrentVisiblePagesRender();
|
|
253
656
|
}) ?? null;
|
|
254
657
|
}
|
|
658
|
+
onViewerWheel(event) {
|
|
659
|
+
if (!event.metaKey && !event.ctrlKey) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (!this.pdfDocument || this.isLoading()) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
event.preventDefault();
|
|
666
|
+
event.stopPropagation();
|
|
667
|
+
const container = this.viewerBody()?.nativeElement;
|
|
668
|
+
const anchor = container ? this.getZoomAnchor(container, event) : null;
|
|
669
|
+
const delta = event.deltaY || event.deltaX;
|
|
670
|
+
const direction = delta < 0 ? 1 : -1;
|
|
671
|
+
const multiplier = Math.max(1, Math.min(6, Math.abs(delta) / 100));
|
|
672
|
+
const nextZoom = this.roundZoom(this.zoom() + direction * this.sanitizeZoomStep() * multiplier);
|
|
673
|
+
if (nextZoom === this.zoom()) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
this.zoomMode.set('custom');
|
|
677
|
+
this.setZoom(nextZoom);
|
|
678
|
+
this.restoreZoomAnchor(anchor);
|
|
679
|
+
}
|
|
255
680
|
selectionRectsForPage(pageNumber) {
|
|
256
681
|
return this.selectionRects().filter((rect) => rect.pageNumber === pageNumber);
|
|
257
682
|
}
|
|
258
|
-
isPageImageFresh(page) {
|
|
259
|
-
return !!page.url && Math.abs((page.renderedScale ?? 0) - page.scale) < 0.0001;
|
|
260
|
-
}
|
|
261
683
|
startTextSelection(event, page) {
|
|
262
684
|
if (event.button !== 0 || page.textGlyphs.length === 0) {
|
|
263
685
|
return;
|
|
@@ -295,17 +717,148 @@ class PdfViewer {
|
|
|
295
717
|
surface.releasePointerCapture?.(event.pointerId);
|
|
296
718
|
}
|
|
297
719
|
}
|
|
720
|
+
loadAnnotationsDataSource(dataSource, context, token) {
|
|
721
|
+
if (!dataSource) {
|
|
722
|
+
this.setAnnotationItems([], token);
|
|
723
|
+
return () => { };
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
if (Array.isArray(dataSource)) {
|
|
727
|
+
this.setAnnotationItems(dataSource, token);
|
|
728
|
+
return () => { };
|
|
729
|
+
}
|
|
730
|
+
if (this.isServerAnnotationDataSource(dataSource)) {
|
|
731
|
+
let isActive = true;
|
|
732
|
+
dataSource.getAnnotations({
|
|
733
|
+
...context,
|
|
734
|
+
successCallback: (annotations) => {
|
|
735
|
+
if (isActive) {
|
|
736
|
+
this.setAnnotationItems(annotations, token);
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
failCallback: () => {
|
|
740
|
+
if (isActive) {
|
|
741
|
+
this.setAnnotationItems([], token);
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
});
|
|
745
|
+
return () => {
|
|
746
|
+
isActive = false;
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const result = typeof dataSource === 'function' ? dataSource(context) : dataSource;
|
|
750
|
+
return this.applyAnnotationDataSourceResult(result, token);
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
this.setAnnotationItems([], token);
|
|
754
|
+
return () => { };
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
applyAnnotationDataSourceResult(result, token) {
|
|
758
|
+
if (isObservable(result)) {
|
|
759
|
+
const subscription = result.subscribe({
|
|
760
|
+
next: (annotations) => this.setAnnotationItems(annotations, token),
|
|
761
|
+
error: () => this.setAnnotationItems([], token),
|
|
762
|
+
});
|
|
763
|
+
return () => subscription.unsubscribe();
|
|
764
|
+
}
|
|
765
|
+
if (this.isPromiseLike(result)) {
|
|
766
|
+
let isActive = true;
|
|
767
|
+
result
|
|
768
|
+
.then((annotations) => {
|
|
769
|
+
if (isActive) {
|
|
770
|
+
this.setAnnotationItems(annotations, token);
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
.catch(() => {
|
|
774
|
+
if (isActive) {
|
|
775
|
+
this.setAnnotationItems([], token);
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
return () => {
|
|
779
|
+
isActive = false;
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
this.setAnnotationItems(result, token);
|
|
783
|
+
return () => { };
|
|
784
|
+
}
|
|
785
|
+
setAnnotationItems(annotations, token) {
|
|
786
|
+
if (token === this.annotationDataSourceToken) {
|
|
787
|
+
this.annotationItems.set(annotations ?? []);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
isServerAnnotationDataSource(dataSource) {
|
|
791
|
+
return typeof dataSource === 'object'
|
|
792
|
+
&& dataSource !== null
|
|
793
|
+
&& 'getAnnotations' in dataSource
|
|
794
|
+
&& typeof dataSource.getAnnotations === 'function';
|
|
795
|
+
}
|
|
796
|
+
isPromiseLike(value) {
|
|
797
|
+
return typeof value === 'object'
|
|
798
|
+
&& value !== null
|
|
799
|
+
&& 'then' in value
|
|
800
|
+
&& typeof value.then === 'function';
|
|
801
|
+
}
|
|
298
802
|
cancelTextSelection() {
|
|
299
803
|
this.selectionStart = null;
|
|
300
804
|
}
|
|
805
|
+
async searchPdf(query, options) {
|
|
806
|
+
const token = ++this.searchToken;
|
|
807
|
+
const keyword = query.trim();
|
|
808
|
+
if (!keyword || !this.engine || !this.pdfDocument) {
|
|
809
|
+
this.pdfSearchResults.set([]);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
const flags = [];
|
|
813
|
+
if (options.caseSensitive) {
|
|
814
|
+
flags.push(MatchFlag.MatchCase);
|
|
815
|
+
}
|
|
816
|
+
if (options.wholeWord) {
|
|
817
|
+
flags.push(MatchFlag.MatchWholeWord);
|
|
818
|
+
}
|
|
819
|
+
try {
|
|
820
|
+
const searchResult = await this.engine.searchAllPages(this.pdfDocument, keyword, { flags }).toPromise();
|
|
821
|
+
if (token !== this.searchToken) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
this.pdfSearchResults.set(searchResult.results.map((result, index) => this.toSearchResultView(result, index)));
|
|
825
|
+
}
|
|
826
|
+
catch (error) {
|
|
827
|
+
if (token === this.searchToken) {
|
|
828
|
+
this.pdfSearchResults.set([]);
|
|
829
|
+
this.error.emit(error);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
toSearchResultView(result, index) {
|
|
834
|
+
const context = result.context;
|
|
835
|
+
const excerpt = [
|
|
836
|
+
context.truncatedLeft ? '...' : '',
|
|
837
|
+
context.before,
|
|
838
|
+
context.match,
|
|
839
|
+
context.after,
|
|
840
|
+
context.truncatedRight ? '...' : '',
|
|
841
|
+
]
|
|
842
|
+
.filter((part) => part.length > 0)
|
|
843
|
+
.join(' ')
|
|
844
|
+
.replace(/\s+/g, ' ')
|
|
845
|
+
.trim();
|
|
846
|
+
return {
|
|
847
|
+
id: `${result.pageIndex}-${result.charIndex}-${result.charCount}-${index}`,
|
|
848
|
+
pageNumber: result.pageIndex + 1,
|
|
849
|
+
excerpt,
|
|
850
|
+
};
|
|
851
|
+
}
|
|
301
852
|
async loadDocument(source, wasmUrl) {
|
|
302
853
|
const token = ++this.loadToken;
|
|
303
854
|
this.renderToken++;
|
|
304
855
|
this.isLoading.set(true);
|
|
305
856
|
this.errorState.set(null);
|
|
306
857
|
this.pageCount.set(0);
|
|
858
|
+
this.searchToken++;
|
|
307
859
|
this.selectionStart = null;
|
|
308
860
|
this.selectionRects.set([]);
|
|
861
|
+
this.pdfSearchResults.set([]);
|
|
309
862
|
this.revokeRenderedPages();
|
|
310
863
|
this.revokeThumbnailPages();
|
|
311
864
|
await this.closeDocument();
|
|
@@ -337,6 +890,10 @@ class PdfViewer {
|
|
|
337
890
|
this.loaded.emit({ pageCount: pdfDocument.pageCount });
|
|
338
891
|
this.initializePageShells(pdfDocument, this.zoom(), this.renderAll(), this.activePage());
|
|
339
892
|
this.schedulePageObserverRefresh();
|
|
893
|
+
await this.searchPdf(this.activeSearchQuery() || this.searchQuery(), {
|
|
894
|
+
caseSensitive: false,
|
|
895
|
+
wholeWord: false,
|
|
896
|
+
});
|
|
340
897
|
await this.renderVisiblePages(token, ++this.renderToken, {
|
|
341
898
|
zoom: this.zoom(),
|
|
342
899
|
activePage: this.activePage(),
|
|
@@ -403,6 +960,21 @@ class PdfViewer {
|
|
|
403
960
|
this.cancelVisiblePageRenderFrame();
|
|
404
961
|
const token = this.loadToken;
|
|
405
962
|
const renderToken = ++this.renderToken;
|
|
963
|
+
const renderDelay = this.getQualityRenderDelay();
|
|
964
|
+
if (renderDelay > 0) {
|
|
965
|
+
this.visiblePageRenderTimeout = targetWindow.setTimeout(() => {
|
|
966
|
+
this.visiblePageRenderTimeout = null;
|
|
967
|
+
this.queueVisiblePageRenderFrame(token, renderToken, options);
|
|
968
|
+
}, renderDelay);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
this.queueVisiblePageRenderFrame(token, renderToken, options);
|
|
972
|
+
}
|
|
973
|
+
queueVisiblePageRenderFrame(token, renderToken, options) {
|
|
974
|
+
const targetWindow = this.document.defaultView;
|
|
975
|
+
if (!targetWindow) {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
406
978
|
this.visiblePageRenderFrame = targetWindow.requestAnimationFrame(() => {
|
|
407
979
|
this.visiblePageRenderFrame = null;
|
|
408
980
|
void this.renderVisiblePages(token, renderToken, options);
|
|
@@ -435,11 +1007,12 @@ class PdfViewer {
|
|
|
435
1007
|
if (currentPage.url && Math.abs((currentPage.renderedScale ?? 0) - renderScale) < 0.0001) {
|
|
436
1008
|
continue;
|
|
437
1009
|
}
|
|
438
|
-
this.patchRenderedPage(pageNumber, { isRendering:
|
|
1010
|
+
this.patchRenderedPage(pageNumber, { isRendering: !currentPage.url });
|
|
439
1011
|
const page = pdfDocument.pages[pageNumber - 1];
|
|
1012
|
+
const rasterOptions = this.getPageRasterRenderOptions(page, renderScale);
|
|
440
1013
|
const blob = await this.engine.renderPage(pdfDocument, page, {
|
|
441
|
-
scaleFactor:
|
|
442
|
-
dpr:
|
|
1014
|
+
scaleFactor: rasterOptions.scaleFactor,
|
|
1015
|
+
dpr: rasterOptions.dpr,
|
|
443
1016
|
withAnnotations: options.withAnnotations,
|
|
444
1017
|
withForms: options.withForms,
|
|
445
1018
|
}).toPromise();
|
|
@@ -471,6 +1044,7 @@ class PdfViewer {
|
|
|
471
1044
|
setZoom(scale) {
|
|
472
1045
|
const nextZoom = this.sanitizeScale(scale);
|
|
473
1046
|
if (this.zoom() !== nextZoom) {
|
|
1047
|
+
this.lastZoomChangeTime = this.getCurrentTime();
|
|
474
1048
|
this.zoom.set(nextZoom);
|
|
475
1049
|
}
|
|
476
1050
|
this.applyInstantZoom(nextZoom);
|
|
@@ -492,9 +1066,7 @@ class PdfViewer {
|
|
|
492
1066
|
const displaySize = this.getPageDisplaySize(pdfPage, nextScale);
|
|
493
1067
|
return {
|
|
494
1068
|
...page,
|
|
495
|
-
url: null,
|
|
496
1069
|
scale: nextScale,
|
|
497
|
-
renderedScale: null,
|
|
498
1070
|
width: displaySize.width,
|
|
499
1071
|
height: displaySize.height,
|
|
500
1072
|
isRendering: false,
|
|
@@ -504,11 +1076,6 @@ class PdfViewer {
|
|
|
504
1076
|
if (!hasChanges) {
|
|
505
1077
|
return;
|
|
506
1078
|
}
|
|
507
|
-
for (const page of pages) {
|
|
508
|
-
if (page.url) {
|
|
509
|
-
this.revokeObjectUrl(page.url);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
1079
|
this.selectionStart = null;
|
|
513
1080
|
this.selectionRects.set([]);
|
|
514
1081
|
this.renderedPages.set(nextPages);
|
|
@@ -805,6 +1372,23 @@ class PdfViewer {
|
|
|
805
1372
|
}
|
|
806
1373
|
URL.revokeObjectURL(url);
|
|
807
1374
|
}
|
|
1375
|
+
getSourceName(source) {
|
|
1376
|
+
if (typeof File !== 'undefined' && source instanceof File && source.name) {
|
|
1377
|
+
return source.name;
|
|
1378
|
+
}
|
|
1379
|
+
if (typeof source !== 'string' || source.trim().length === 0) {
|
|
1380
|
+
return 'Document.pdf';
|
|
1381
|
+
}
|
|
1382
|
+
try {
|
|
1383
|
+
const url = new URL(source, this.document.baseURI);
|
|
1384
|
+
const pathName = url.pathname.split('/').filter(Boolean).pop();
|
|
1385
|
+
return pathName ? decodeURIComponent(pathName) : 'Document.pdf';
|
|
1386
|
+
}
|
|
1387
|
+
catch {
|
|
1388
|
+
const pathName = source.split('?')[0]?.split('#')[0]?.split('/').filter(Boolean).pop();
|
|
1389
|
+
return pathName ? decodeURIComponent(pathName) : 'Document.pdf';
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
808
1392
|
sanitizePage(pageNumber) {
|
|
809
1393
|
return this.clamp(Math.trunc(Number.isFinite(pageNumber) ? pageNumber : 1), 1, Math.max(this.pageCount(), 1));
|
|
810
1394
|
}
|
|
@@ -815,15 +1399,43 @@ class PdfViewer {
|
|
|
815
1399
|
roundZoom(value) {
|
|
816
1400
|
return this.sanitizeScale(Math.round(this.sanitizeScale(value) * 100) / 100);
|
|
817
1401
|
}
|
|
1402
|
+
floorFitZoom(value) {
|
|
1403
|
+
return this.sanitizeScale(Math.floor(this.sanitizeScale(value) * 100) / 100);
|
|
1404
|
+
}
|
|
818
1405
|
clamp(value, min, max) {
|
|
819
1406
|
return Math.min(Math.max(value, min), max);
|
|
820
1407
|
}
|
|
821
1408
|
roundCssPixel(value) {
|
|
822
1409
|
return Math.round(value * 100) / 100;
|
|
823
1410
|
}
|
|
1411
|
+
parseCssPixel(value) {
|
|
1412
|
+
const parsed = Number.parseFloat(value);
|
|
1413
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
1414
|
+
}
|
|
824
1415
|
getDevicePixelRatio() {
|
|
825
1416
|
return this.document.defaultView?.devicePixelRatio || 1;
|
|
826
1417
|
}
|
|
1418
|
+
getPageRasterRenderOptions(page, scale) {
|
|
1419
|
+
const devicePixelRatio = Math.max(1, this.getDevicePixelRatio());
|
|
1420
|
+
const pageSize = this.getPageBaseSize(page);
|
|
1421
|
+
const maxRenderPixels = this.sanitizePositiveNumber(this.maxRenderPixels(), 128_000_000);
|
|
1422
|
+
const maxRenderDimension = this.sanitizePositiveNumber(this.maxRenderDimension(), 12_000);
|
|
1423
|
+
const targetEffectiveScale = scale * devicePixelRatio;
|
|
1424
|
+
const dimensionEffectiveScale = maxRenderDimension / Math.max(pageSize.width, pageSize.height);
|
|
1425
|
+
const pixelEffectiveScale = Math.sqrt(maxRenderPixels / (pageSize.width * pageSize.height));
|
|
1426
|
+
const effectiveScale = this.clamp(Math.min(targetEffectiveScale, dimensionEffectiveScale, pixelEffectiveScale), 0.05, targetEffectiveScale);
|
|
1427
|
+
const dprAtLayoutScale = effectiveScale / scale;
|
|
1428
|
+
if (dprAtLayoutScale >= 1) {
|
|
1429
|
+
return {
|
|
1430
|
+
scaleFactor: scale,
|
|
1431
|
+
dpr: this.clamp(dprAtLayoutScale, 1, devicePixelRatio),
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
return {
|
|
1435
|
+
scaleFactor: effectiveScale,
|
|
1436
|
+
dpr: 1,
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
827
1439
|
createDocumentId() {
|
|
828
1440
|
return `ngs-pdf-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
829
1441
|
}
|
|
@@ -849,12 +1461,82 @@ class PdfViewer {
|
|
|
849
1461
|
this.pageObserverFrame = null;
|
|
850
1462
|
}
|
|
851
1463
|
cancelVisiblePageRenderFrame() {
|
|
1464
|
+
if (this.visiblePageRenderTimeout !== null) {
|
|
1465
|
+
this.document.defaultView?.clearTimeout(this.visiblePageRenderTimeout);
|
|
1466
|
+
this.visiblePageRenderTimeout = null;
|
|
1467
|
+
}
|
|
852
1468
|
if (this.visiblePageRenderFrame === null) {
|
|
853
1469
|
return;
|
|
854
1470
|
}
|
|
855
1471
|
this.document.defaultView?.cancelAnimationFrame(this.visiblePageRenderFrame);
|
|
856
1472
|
this.visiblePageRenderFrame = null;
|
|
857
1473
|
}
|
|
1474
|
+
getCurrentTime() {
|
|
1475
|
+
return this.document.defaultView?.performance?.now() ?? Date.now();
|
|
1476
|
+
}
|
|
1477
|
+
getQualityRenderDelay() {
|
|
1478
|
+
const elapsed = this.getCurrentTime() - this.lastZoomChangeTime;
|
|
1479
|
+
if (elapsed >= this.qualityRenderZoomIdleDelay) {
|
|
1480
|
+
return 0;
|
|
1481
|
+
}
|
|
1482
|
+
return Math.max(0, this.qualityRenderZoomIdleDelay - elapsed);
|
|
1483
|
+
}
|
|
1484
|
+
getZoomAnchor(container, event) {
|
|
1485
|
+
if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) {
|
|
1486
|
+
return null;
|
|
1487
|
+
}
|
|
1488
|
+
const targetDocument = container.ownerDocument;
|
|
1489
|
+
const targetElement = targetDocument.elementFromPoint(event.clientX, event.clientY);
|
|
1490
|
+
const pageElement = targetElement?.closest('[data-ngs-pdf-page]');
|
|
1491
|
+
if (!pageElement || !container.contains(pageElement)) {
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
const containerRect = container.getBoundingClientRect();
|
|
1495
|
+
const pageRect = pageElement.getBoundingClientRect();
|
|
1496
|
+
const pageNumber = Number(pageElement.dataset['ngsPdfPage']);
|
|
1497
|
+
if (!Number.isFinite(pageNumber) || pageRect.width <= 0 || pageRect.height <= 0) {
|
|
1498
|
+
return null;
|
|
1499
|
+
}
|
|
1500
|
+
return {
|
|
1501
|
+
container,
|
|
1502
|
+
pageNumber,
|
|
1503
|
+
relativeX: this.clamp((event.clientX - pageRect.left) / pageRect.width, 0, 1),
|
|
1504
|
+
relativeY: this.clamp((event.clientY - pageRect.top) / pageRect.height, 0, 1),
|
|
1505
|
+
viewportX: event.clientX - containerRect.left,
|
|
1506
|
+
viewportY: event.clientY - containerRect.top,
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
restoreZoomAnchor(anchor) {
|
|
1510
|
+
if (!anchor) {
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
const targetWindow = this.document.defaultView;
|
|
1514
|
+
const restore = () => {
|
|
1515
|
+
const pageElement = anchor.container.querySelector(`[data-ngs-pdf-page="${anchor.pageNumber}"]`);
|
|
1516
|
+
if (!pageElement) {
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
const containerRect = anchor.container.getBoundingClientRect();
|
|
1520
|
+
const pageRect = pageElement.getBoundingClientRect();
|
|
1521
|
+
const targetLeft = anchor.container.scrollLeft +
|
|
1522
|
+
pageRect.left -
|
|
1523
|
+
containerRect.left +
|
|
1524
|
+
pageRect.width * anchor.relativeX -
|
|
1525
|
+
anchor.viewportX;
|
|
1526
|
+
const targetTop = anchor.container.scrollTop +
|
|
1527
|
+
pageRect.top -
|
|
1528
|
+
containerRect.top +
|
|
1529
|
+
pageRect.height * anchor.relativeY -
|
|
1530
|
+
anchor.viewportY;
|
|
1531
|
+
anchor.container.scrollLeft = this.clamp(targetLeft, 0, Math.max(0, anchor.container.scrollWidth - anchor.container.clientWidth));
|
|
1532
|
+
anchor.container.scrollTop = this.clamp(targetTop, 0, Math.max(0, anchor.container.scrollHeight - anchor.container.clientHeight));
|
|
1533
|
+
};
|
|
1534
|
+
if (!targetWindow) {
|
|
1535
|
+
restore();
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
targetWindow.requestAnimationFrame(restore);
|
|
1539
|
+
}
|
|
858
1540
|
disconnectPageObserver() {
|
|
859
1541
|
this.cancelPageObserverFrame();
|
|
860
1542
|
this.pageIntersectionObserver?.disconnect();
|
|
@@ -924,6 +1606,32 @@ class PdfViewer {
|
|
|
924
1606
|
}
|
|
925
1607
|
return true;
|
|
926
1608
|
}
|
|
1609
|
+
getFitScale(mode) {
|
|
1610
|
+
const container = this.viewerBody()?.nativeElement;
|
|
1611
|
+
const page = this.pdfDocument?.pages[this.clamp(this.activePage(), 1, Math.max(this.pageCount(), 1)) - 1];
|
|
1612
|
+
if (!container || !page) {
|
|
1613
|
+
return null;
|
|
1614
|
+
}
|
|
1615
|
+
const pageSize = this.getPageBaseSize(page);
|
|
1616
|
+
const pagesElement = container.querySelector('.pdf-viewer-pages');
|
|
1617
|
+
const computedStyle = pagesElement && this.document.defaultView
|
|
1618
|
+
? this.document.defaultView.getComputedStyle(pagesElement)
|
|
1619
|
+
: null;
|
|
1620
|
+
const horizontalPadding = computedStyle
|
|
1621
|
+
? this.parseCssPixel(computedStyle.paddingLeft) + this.parseCssPixel(computedStyle.paddingRight)
|
|
1622
|
+
: 0;
|
|
1623
|
+
const verticalPadding = computedStyle
|
|
1624
|
+
? this.parseCssPixel(computedStyle.paddingTop) + this.parseCssPixel(computedStyle.paddingBottom)
|
|
1625
|
+
: 0;
|
|
1626
|
+
const fitAllowance = 1;
|
|
1627
|
+
const availableWidth = Math.max(1, container.clientWidth - horizontalPadding - fitAllowance);
|
|
1628
|
+
const availableHeight = Math.max(1, container.clientHeight - verticalPadding - fitAllowance);
|
|
1629
|
+
const widthScale = availableWidth / pageSize.width;
|
|
1630
|
+
if (mode === 'fit-width') {
|
|
1631
|
+
return this.floorFitZoom(widthScale);
|
|
1632
|
+
}
|
|
1633
|
+
return this.floorFitZoom(Math.min(widthScale, availableHeight / pageSize.height));
|
|
1634
|
+
}
|
|
927
1635
|
getScaleBounds() {
|
|
928
1636
|
const min = this.sanitizePositiveNumber(this.minScale(), 0.2);
|
|
929
1637
|
const max = this.sanitizePositiveNumber(this.maxScale(), 60);
|
|
@@ -936,12 +1644,19 @@ class PdfViewer {
|
|
|
936
1644
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
937
1645
|
}
|
|
938
1646
|
getPageDisplaySize(page, scale) {
|
|
1647
|
+
const pageSize = this.getPageBaseSize(page);
|
|
1648
|
+
return {
|
|
1649
|
+
width: Math.max(1, Math.round(pageSize.width * scale)),
|
|
1650
|
+
height: Math.max(1, Math.round(pageSize.height * scale)),
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1653
|
+
getPageBaseSize(page) {
|
|
939
1654
|
const isRotatedSideways = page.rotation === 1 || page.rotation === 3;
|
|
940
1655
|
const width = isRotatedSideways ? page.size.height : page.size.width;
|
|
941
1656
|
const height = isRotatedSideways ? page.size.width : page.size.height;
|
|
942
1657
|
return {
|
|
943
|
-
width: Math.max(1,
|
|
944
|
-
height: Math.max(1,
|
|
1658
|
+
width: Math.max(1, width),
|
|
1659
|
+
height: Math.max(1, height),
|
|
945
1660
|
};
|
|
946
1661
|
}
|
|
947
1662
|
scrollToPage(pageNumber) {
|
|
@@ -958,6 +1673,7 @@ class PdfViewer {
|
|
|
958
1673
|
this.startProgrammaticScrollLock(pageNumber, scrollDistance);
|
|
959
1674
|
container.scrollTo({
|
|
960
1675
|
top: nextScrollTop,
|
|
1676
|
+
left: this.getPageScrollLeft(container, target),
|
|
961
1677
|
behavior: 'smooth',
|
|
962
1678
|
});
|
|
963
1679
|
}
|
|
@@ -1046,6 +1762,12 @@ class PdfViewer {
|
|
|
1046
1762
|
getPageScrollTop(container, target) {
|
|
1047
1763
|
return this.getElementScrollTop(container, target, 'start');
|
|
1048
1764
|
}
|
|
1765
|
+
getPageScrollLeft(container, target) {
|
|
1766
|
+
const containerRect = container.getBoundingClientRect();
|
|
1767
|
+
const targetRect = target.getBoundingClientRect();
|
|
1768
|
+
const targetLeft = container.scrollLeft + targetRect.left - containerRect.left;
|
|
1769
|
+
return this.clamp(targetLeft, 0, Math.max(0, container.scrollWidth - container.clientWidth));
|
|
1770
|
+
}
|
|
1049
1771
|
getElementScrollTop(container, target, align) {
|
|
1050
1772
|
const containerRect = container.getBoundingClientRect();
|
|
1051
1773
|
const targetRect = target.getBoundingClientRect();
|
|
@@ -1108,13 +1830,14 @@ class PdfViewer {
|
|
|
1108
1830
|
this.programmaticScrollTargetPage = null;
|
|
1109
1831
|
}
|
|
1110
1832
|
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 });
|
|
1833
|
+
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 />\n </ngs-toolbar-item>\n <ngs-toolbar-title class=\"min-w-0 flex-1 truncate text-sm! 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-5\">\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-16 cursor-pointer border-0 bg-transparent p-0 text-center text-sm font-bold 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 />\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-24 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 (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 (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 group-[.is-active]:border-border group-[.is-active]:bg-primary-container group-[.is-active]:shadow-xs group-[.is-active]:ring-2 group-[.is-active]:ring-primary-container\" 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 class=\"pdf-viewer-pages grid min-h-full min-w-full w-max content-start grid-flow-row box-border justify-items-center gap-6 p-6\">\n @for (pdfPage of renderedPages(); 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 </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;display:block;height:var(--ngs-pdf-viewer-height);min-height:var(--ngs-pdf-viewer-min-height);overflow:hidden}: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
1834
|
}
|
|
1113
1835
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PdfViewer, decorators: [{
|
|
1114
1836
|
type: Component,
|
|
1115
|
-
args: [{ selector: 'ngs-pdf-viewer', exportAs: 'ngsPdfViewer',
|
|
1837
|
+
args: [{ selector: 'ngs-pdf-viewer', exportAs: 'ngsPdfViewer', imports: [
|
|
1116
1838
|
BlockLoader,
|
|
1117
1839
|
Button,
|
|
1840
|
+
Divider,
|
|
1118
1841
|
Icon,
|
|
1119
1842
|
ImagePlaceholder,
|
|
1120
1843
|
Menu,
|
|
@@ -1123,21 +1846,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
1123
1846
|
MenuItem,
|
|
1124
1847
|
MenuTrigger,
|
|
1125
1848
|
Panel,
|
|
1849
|
+
PanelAside,
|
|
1126
1850
|
PanelContent,
|
|
1127
1851
|
PanelHeader,
|
|
1128
1852
|
PanelSidebar,
|
|
1853
|
+
Toolbar,
|
|
1854
|
+
ToolbarItem,
|
|
1855
|
+
ToolbarSpacer,
|
|
1856
|
+
ToolbarTitle,
|
|
1857
|
+
PdfViewerAnnotations,
|
|
1858
|
+
PdfViewerSearch,
|
|
1129
1859
|
], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1130
1860
|
class: 'ngs-pdf-viewer not-prose',
|
|
1131
1861
|
'[class.is-loading]': 'isLoading()',
|
|
1132
1862
|
'[class.has-toolbar]': 'showToolbar()',
|
|
1133
1863
|
'[class.has-page-list]': 'isPageListVisible()',
|
|
1134
1864
|
'[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"] }] } });
|
|
1865
|
+
}, 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 />\n </ngs-toolbar-item>\n <ngs-toolbar-title class=\"min-w-0 flex-1 truncate text-sm! 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-5\">\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-16 cursor-pointer border-0 bg-transparent p-0 text-center text-sm font-bold 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 />\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-24 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 (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 (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 group-[.is-active]:border-border group-[.is-active]:bg-primary-container group-[.is-active]:shadow-xs group-[.is-active]:ring-2 group-[.is-active]:ring-primary-container\" 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 class=\"pdf-viewer-pages grid min-h-full min-w-full w-max content-start grid-flow-row box-border justify-items-center gap-6 p-6\">\n @for (pdfPage of renderedPages(); 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 </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;display:block;height:var(--ngs-pdf-viewer-height);min-height:var(--ngs-pdf-viewer-min-height);overflow:hidden}:host .pdf-viewer-page__selection-layer{forced-color-adjust:none}\n"] }]
|
|
1866
|
+
}], 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
1867
|
|
|
1138
1868
|
/**
|
|
1139
1869
|
* Generated bundle index. Do not edit.
|
|
1140
1870
|
*/
|
|
1141
1871
|
|
|
1142
|
-
export { PdfViewer, PdfViewerEngineService };
|
|
1872
|
+
export { PdfViewer, PdfViewerAnnotationDef, PdfViewerAnnotations, PdfViewerEngineService, PdfViewerSearch };
|
|
1143
1873
|
//# sourceMappingURL=ngstarter-ui-components-pdf-viewer.mjs.map
|