@memberjunction/ng-artifacts 5.23.0 → 5.25.0
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/dist/lib/artifacts.module.d.ts +16 -11
- package/dist/lib/artifacts.module.d.ts.map +1 -1
- package/dist/lib/artifacts.module.js +40 -8
- package/dist/lib/artifacts.module.js.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.d.ts +3 -0
- package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
- package/dist/lib/components/artifact-viewer-panel.component.js +13 -2
- package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
- package/dist/lib/components/base-artifact-viewer.component.d.ts +6 -0
- package/dist/lib/components/base-artifact-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/base-artifact-viewer.component.js +15 -0
- package/dist/lib/components/base-artifact-viewer.component.js.map +1 -1
- package/dist/lib/components/file-artifact-toolbar.component.d.ts +42 -0
- package/dist/lib/components/file-artifact-toolbar.component.d.ts.map +1 -0
- package/dist/lib/components/file-artifact-toolbar.component.js +249 -0
- package/dist/lib/components/file-artifact-toolbar.component.js.map +1 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts +40 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js +223 -0
- package/dist/lib/components/plugins/docx-artifact-viewer.component.js.map +1 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts +68 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js +340 -0
- package/dist/lib/components/plugins/pdf-artifact-viewer.component.js.map +1 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts +44 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.d.ts.map +1 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js +255 -0
- package/dist/lib/components/plugins/xlsx-artifact-viewer.component.js.map +1 -0
- package/dist/lib/services/artifact-file.service.d.ts +33 -0
- package/dist/lib/services/artifact-file.service.d.ts.map +1 -0
- package/dist/lib/services/artifact-file.service.js +87 -0
- package/dist/lib/services/artifact-file.service.js.map +1 -0
- package/dist/public-api.d.ts +5 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +6 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +20 -16
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Component, SecurityContext } from '@angular/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
import * as i1 from "../../services/artifact-file.service";
|
|
12
|
+
import * as i2 from "@angular/platform-browser";
|
|
13
|
+
import * as i3 from "../file-artifact-toolbar.component";
|
|
14
|
+
function DocxArtifactViewerComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
15
|
+
i0.ɵɵelementStart(0, "div", 3);
|
|
16
|
+
i0.ɵɵelement(1, "i", 6);
|
|
17
|
+
i0.ɵɵelementStart(2, "span");
|
|
18
|
+
i0.ɵɵtext(3, "Loading document\u2026");
|
|
19
|
+
i0.ɵɵelementEnd()();
|
|
20
|
+
} }
|
|
21
|
+
function DocxArtifactViewerComponent_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
22
|
+
i0.ɵɵelementStart(0, "div", 4);
|
|
23
|
+
i0.ɵɵelement(1, "i", 7);
|
|
24
|
+
i0.ɵɵelementStart(2, "span");
|
|
25
|
+
i0.ɵɵtext(3);
|
|
26
|
+
i0.ɵɵelementEnd()();
|
|
27
|
+
} if (rf & 2) {
|
|
28
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
29
|
+
i0.ɵɵadvance(3);
|
|
30
|
+
i0.ɵɵtextInterpolate(ctx_r0.errorMessage);
|
|
31
|
+
} }
|
|
32
|
+
function DocxArtifactViewerComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
33
|
+
i0.ɵɵelement(0, "div", 5);
|
|
34
|
+
} if (rf & 2) {
|
|
35
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
36
|
+
i0.ɵɵproperty("innerHTML", ctx_r0.safeHtml, i0.ɵɵsanitizeHtml);
|
|
37
|
+
} }
|
|
38
|
+
/**
|
|
39
|
+
* Viewer plugin for Word (DOCX) artifact versions stored in MJStorage.
|
|
40
|
+
*
|
|
41
|
+
* Downloads the binary file, converts it to HTML using mammoth.js, then
|
|
42
|
+
* sanitizes the HTML before binding it with [innerHTML].
|
|
43
|
+
*
|
|
44
|
+
* Printing opens the sanitized content in a child window so the browser's
|
|
45
|
+
* native print dialog renders the Word document faithfully.
|
|
46
|
+
*/
|
|
47
|
+
let DocxArtifactViewerComponent = class DocxArtifactViewerComponent extends BaseArtifactViewerPluginComponent {
|
|
48
|
+
fileService;
|
|
49
|
+
sanitizer;
|
|
50
|
+
cdr;
|
|
51
|
+
isLoading = true;
|
|
52
|
+
isDownloading = false;
|
|
53
|
+
errorMessage = '';
|
|
54
|
+
safeHtml = '';
|
|
55
|
+
/** Raw HTML produced by mammoth — kept for print. */
|
|
56
|
+
rawHtml = '';
|
|
57
|
+
downloadUrl = '';
|
|
58
|
+
constructor(fileService, sanitizer, cdr) {
|
|
59
|
+
super();
|
|
60
|
+
this.fileService = fileService;
|
|
61
|
+
this.sanitizer = sanitizer;
|
|
62
|
+
this.cdr = cdr;
|
|
63
|
+
}
|
|
64
|
+
get hasDisplayContent() { return true; }
|
|
65
|
+
async ngOnInit() {
|
|
66
|
+
await this.loadDocument();
|
|
67
|
+
}
|
|
68
|
+
async onDownload() {
|
|
69
|
+
if (!this.downloadUrl || this.isDownloading) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.isDownloading = true;
|
|
73
|
+
this.cdr.markForCheck();
|
|
74
|
+
try {
|
|
75
|
+
await this.triggerBrowserDownload(this.downloadUrl, this.artifactVersion?.FileName || 'document.docx');
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
this.isDownloading = false;
|
|
79
|
+
this.cdr.markForCheck();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
onPrint() {
|
|
83
|
+
if (!this.rawHtml) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.openPrintWindow(this.rawHtml);
|
|
87
|
+
}
|
|
88
|
+
// ─── Private helpers ────────────────────────────────────────────────────────
|
|
89
|
+
async loadDocument() {
|
|
90
|
+
if (!this.artifactVersion?.ID) {
|
|
91
|
+
this.showError('No artifact version provided.');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
let arrayBuffer;
|
|
96
|
+
if (this.artifactVersion.ContentMode === 'File') {
|
|
97
|
+
// File-backed: download from storage via pre-auth URL
|
|
98
|
+
this.downloadUrl = await this.fileService.getDownloadUrl(this.artifactVersion.ID);
|
|
99
|
+
arrayBuffer = await this.fetchAsArrayBuffer(this.downloadUrl);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Inline: content is a base64 data URL stored in the artifact version
|
|
103
|
+
const content = this.artifactVersion.Content;
|
|
104
|
+
if (!content) {
|
|
105
|
+
this.showError('Artifact has no content.');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
arrayBuffer = this.fileService.dataUrlToArrayBuffer(content);
|
|
109
|
+
// Create an object URL for download support
|
|
110
|
+
this.downloadUrl = this.fileService.dataUrlToObjectUrl(content, this.artifactVersion.MimeType || 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
|
111
|
+
}
|
|
112
|
+
const html = await this.convertDocxToHtml(arrayBuffer);
|
|
113
|
+
this.rawHtml = html;
|
|
114
|
+
const sanitized = this.sanitizer.sanitize(SecurityContext.HTML, html) ?? '';
|
|
115
|
+
this.safeHtml = sanitized;
|
|
116
|
+
this.isLoading = false;
|
|
117
|
+
this.cdr.markForCheck();
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
this.showError(`Could not load document: ${err instanceof Error ? err.message : String(err)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async convertDocxToHtml(arrayBuffer) {
|
|
124
|
+
const mammoth = await import('mammoth');
|
|
125
|
+
const result = await mammoth.convertToHtml({ arrayBuffer });
|
|
126
|
+
if (result.messages?.length) {
|
|
127
|
+
// Log warnings from mammoth (e.g. unsupported features) without breaking display
|
|
128
|
+
result.messages.forEach(m => console.warn('[DocxViewer] mammoth:', m.message));
|
|
129
|
+
}
|
|
130
|
+
return result.value;
|
|
131
|
+
}
|
|
132
|
+
async fetchAsArrayBuffer(url) {
|
|
133
|
+
const response = await fetch(url);
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
throw new Error(`HTTP ${response.status} fetching file`);
|
|
136
|
+
}
|
|
137
|
+
return response.arrayBuffer();
|
|
138
|
+
}
|
|
139
|
+
/** Open a minimal print window containing only the document HTML. */
|
|
140
|
+
openPrintWindow(html) {
|
|
141
|
+
const printWindow = window.open('', '_blank', 'width=900,height=700');
|
|
142
|
+
if (!printWindow) {
|
|
143
|
+
console.warn('[DocxViewer] Popup was blocked — cannot print');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
printWindow.document.write(`
|
|
147
|
+
<!DOCTYPE html>
|
|
148
|
+
<html>
|
|
149
|
+
<head>
|
|
150
|
+
<meta charset="utf-8">
|
|
151
|
+
<title>${this.artifactVersion?.FileName ?? 'Document'}</title>
|
|
152
|
+
<style>
|
|
153
|
+
body { font-family: Georgia, serif; font-size: 13pt; line-height: 1.6; margin: 2cm; color: #000; }
|
|
154
|
+
table { border-collapse: collapse; width: 100%; }
|
|
155
|
+
td, th { border: 1px solid #999; padding: 4px 8px; }
|
|
156
|
+
th { background: #eee; font-weight: bold; }
|
|
157
|
+
img { max-width: 100%; }
|
|
158
|
+
</style>
|
|
159
|
+
</head>
|
|
160
|
+
<body>${html}</body>
|
|
161
|
+
</html>
|
|
162
|
+
`);
|
|
163
|
+
printWindow.document.close();
|
|
164
|
+
printWindow.focus();
|
|
165
|
+
printWindow.print();
|
|
166
|
+
printWindow.close();
|
|
167
|
+
}
|
|
168
|
+
showError(message) {
|
|
169
|
+
this.isLoading = false;
|
|
170
|
+
this.errorMessage = message;
|
|
171
|
+
this.cdr.markForCheck();
|
|
172
|
+
}
|
|
173
|
+
static ɵfac = function DocxArtifactViewerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || DocxArtifactViewerComponent)(i0.ɵɵdirectiveInject(i1.ArtifactFileService), i0.ɵɵdirectiveInject(i2.DomSanitizer), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
174
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DocxArtifactViewerComponent, selectors: [["mj-docx-artifact-viewer"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 4, consts: [[1, "docx-viewer"], [3, "download", "print", "fileName", "isDownloading", "showPrint"], [1, "docx-viewer__body"], [1, "docx-viewer__state"], [1, "docx-viewer__state", "docx-viewer__state--error"], [1, "docx-viewer__content", 3, "innerHTML"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-exclamation-triangle"]], template: function DocxArtifactViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
175
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "mj-file-artifact-toolbar", 1);
|
|
176
|
+
i0.ɵɵlistener("download", function DocxArtifactViewerComponent_Template_mj_file_artifact_toolbar_download_1_listener() { return ctx.onDownload(); })("print", function DocxArtifactViewerComponent_Template_mj_file_artifact_toolbar_print_1_listener() { return ctx.onPrint(); });
|
|
177
|
+
i0.ɵɵelementEnd();
|
|
178
|
+
i0.ɵɵelementStart(2, "div", 2);
|
|
179
|
+
i0.ɵɵconditionalCreate(3, DocxArtifactViewerComponent_Conditional_3_Template, 4, 0, "div", 3)(4, DocxArtifactViewerComponent_Conditional_4_Template, 4, 1, "div", 4)(5, DocxArtifactViewerComponent_Conditional_5_Template, 1, 1, "div", 5);
|
|
180
|
+
i0.ɵɵelementEnd()();
|
|
181
|
+
} if (rf & 2) {
|
|
182
|
+
i0.ɵɵadvance();
|
|
183
|
+
i0.ɵɵproperty("fileName", (ctx.artifactVersion == null ? null : ctx.artifactVersion.FileName) || "document.docx")("isDownloading", ctx.isDownloading)("showPrint", true);
|
|
184
|
+
i0.ɵɵadvance(2);
|
|
185
|
+
i0.ɵɵconditional(ctx.isLoading ? 3 : ctx.errorMessage ? 4 : 5);
|
|
186
|
+
} }, dependencies: [i3.FileArtifactToolbarComponent], styles: [".docx-viewer[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .docx-viewer__body[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .docx-viewer__state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 100%;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .docx-viewer__state--error[_ngcontent-%COMP%] {\n color: var(--mj-status-error-text);\n }\n\n .docx-viewer__content[_ngcontent-%COMP%] {\n padding: 32px 48px;\n max-width: 900px;\n margin: 0 auto;\n color: var(--mj-text-primary);\n font-size: 14px;\n line-height: 1.7;\n }\n\n \n\n .docx-viewer__content[_ngcontent-%COMP%] :is(h1[_ngcontent-%COMP%], h2[_ngcontent-%COMP%], h3[_ngcontent-%COMP%], h4[_ngcontent-%COMP%], h5[_ngcontent-%COMP%], h6[_ngcontent-%COMP%]) {\n color: var(--mj-text-primary);\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n }\n\n .docx-viewer__content[_ngcontent-%COMP%] table[_ngcontent-%COMP%] {\n border-collapse: collapse;\n width: 100%;\n margin: 1em 0;\n }\n\n .docx-viewer__content[_ngcontent-%COMP%] :is(td[_ngcontent-%COMP%], th[_ngcontent-%COMP%]) {\n border: 1px solid var(--mj-border-default);\n padding: 6px 10px;\n }\n\n .docx-viewer__content[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n font-weight: 600;\n }\n\n .docx-viewer__content[_ngcontent-%COMP%] a[_ngcontent-%COMP%] {\n color: var(--mj-text-link);\n }\n\n .docx-viewer__content[_ngcontent-%COMP%] img[_ngcontent-%COMP%] {\n max-width: 100%;\n height: auto;\n }"] });
|
|
187
|
+
};
|
|
188
|
+
DocxArtifactViewerComponent = __decorate([
|
|
189
|
+
RegisterClass(BaseArtifactViewerPluginComponent, 'DocxArtifactViewerPlugin')
|
|
190
|
+
], DocxArtifactViewerComponent);
|
|
191
|
+
export { DocxArtifactViewerComponent };
|
|
192
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DocxArtifactViewerComponent, [{
|
|
193
|
+
type: Component,
|
|
194
|
+
args: [{ standalone: false, selector: 'mj-docx-artifact-viewer', template: `
|
|
195
|
+
<div class="docx-viewer">
|
|
196
|
+
<mj-file-artifact-toolbar
|
|
197
|
+
[fileName]="artifactVersion?.FileName || 'document.docx'"
|
|
198
|
+
[isDownloading]="isDownloading"
|
|
199
|
+
[showPrint]="true"
|
|
200
|
+
(download)="onDownload()"
|
|
201
|
+
(print)="onPrint()">
|
|
202
|
+
</mj-file-artifact-toolbar>
|
|
203
|
+
|
|
204
|
+
<div class="docx-viewer__body">
|
|
205
|
+
@if (isLoading) {
|
|
206
|
+
<div class="docx-viewer__state">
|
|
207
|
+
<i class="fas fa-spinner fa-spin"></i>
|
|
208
|
+
<span>Loading document…</span>
|
|
209
|
+
</div>
|
|
210
|
+
} @else if (errorMessage) {
|
|
211
|
+
<div class="docx-viewer__state docx-viewer__state--error">
|
|
212
|
+
<i class="fas fa-exclamation-triangle"></i>
|
|
213
|
+
<span>{{ errorMessage }}</span>
|
|
214
|
+
</div>
|
|
215
|
+
} @else {
|
|
216
|
+
<div class="docx-viewer__content" [innerHTML]="safeHtml"></div>
|
|
217
|
+
}
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
`, styles: ["\n .docx-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .docx-viewer__body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .docx-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 100%;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .docx-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n\n .docx-viewer__content {\n padding: 32px 48px;\n max-width: 900px;\n margin: 0 auto;\n color: var(--mj-text-primary);\n font-size: 14px;\n line-height: 1.7;\n }\n\n /* Normalize headings produced by mammoth */\n .docx-viewer__content :is(h1, h2, h3, h4, h5, h6) {\n color: var(--mj-text-primary);\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n }\n\n .docx-viewer__content table {\n border-collapse: collapse;\n width: 100%;\n margin: 1em 0;\n }\n\n .docx-viewer__content :is(td, th) {\n border: 1px solid var(--mj-border-default);\n padding: 6px 10px;\n }\n\n .docx-viewer__content th {\n background: var(--mj-bg-surface-card);\n font-weight: 600;\n }\n\n .docx-viewer__content a {\n color: var(--mj-text-link);\n }\n\n .docx-viewer__content img {\n max-width: 100%;\n height: auto;\n }\n "] }]
|
|
221
|
+
}], () => [{ type: i1.ArtifactFileService }, { type: i2.DomSanitizer }, { type: i0.ChangeDetectorRef }], null); })();
|
|
222
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(DocxArtifactViewerComponent, { className: "DocxArtifactViewerComponent", filePath: "src/lib/components/plugins/docx-artifact-viewer.component.ts", lineNumber: 118 }); })();
|
|
223
|
+
//# sourceMappingURL=docx-artifact-viewer.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docx-artifact-viewer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/plugins/docx-artifact-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, ChangeDetectorRef, SecurityContext } from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { RegisterClass } from '@memberjunction/global';\nimport { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';\nimport { ArtifactFileService } from '../../services/artifact-file.service';\n\n/**\n * Viewer plugin for Word (DOCX) artifact versions stored in MJStorage.\n *\n * Downloads the binary file, converts it to HTML using mammoth.js, then\n * sanitizes the HTML before binding it with [innerHTML].\n *\n * Printing opens the sanitized content in a child window so the browser's\n * native print dialog renders the Word document faithfully.\n */\n@Component({\n standalone: false,\n selector: 'mj-docx-artifact-viewer',\n template: `\n <div class=\"docx-viewer\">\n <mj-file-artifact-toolbar\n [fileName]=\"artifactVersion?.FileName || 'document.docx'\"\n [isDownloading]=\"isDownloading\"\n [showPrint]=\"true\"\n (download)=\"onDownload()\"\n (print)=\"onPrint()\">\n </mj-file-artifact-toolbar>\n\n <div class=\"docx-viewer__body\">\n @if (isLoading) {\n <div class=\"docx-viewer__state\">\n <i class=\"fas fa-spinner fa-spin\"></i>\n <span>Loading document…</span>\n </div>\n } @else if (errorMessage) {\n <div class=\"docx-viewer__state docx-viewer__state--error\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n </div>\n } @else {\n <div class=\"docx-viewer__content\" [innerHTML]=\"safeHtml\"></div>\n }\n </div>\n </div>\n `,\n styles: [`\n .docx-viewer {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .docx-viewer__body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n }\n\n .docx-viewer__state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 100%;\n color: var(--mj-text-muted);\n font-size: 14px;\n }\n\n .docx-viewer__state--error {\n color: var(--mj-status-error-text);\n }\n\n .docx-viewer__content {\n padding: 32px 48px;\n max-width: 900px;\n margin: 0 auto;\n color: var(--mj-text-primary);\n font-size: 14px;\n line-height: 1.7;\n }\n\n /* Normalize headings produced by mammoth */\n .docx-viewer__content :is(h1, h2, h3, h4, h5, h6) {\n color: var(--mj-text-primary);\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n }\n\n .docx-viewer__content table {\n border-collapse: collapse;\n width: 100%;\n margin: 1em 0;\n }\n\n .docx-viewer__content :is(td, th) {\n border: 1px solid var(--mj-border-default);\n padding: 6px 10px;\n }\n\n .docx-viewer__content th {\n background: var(--mj-bg-surface-card);\n font-weight: 600;\n }\n\n .docx-viewer__content a {\n color: var(--mj-text-link);\n }\n\n .docx-viewer__content img {\n max-width: 100%;\n height: auto;\n }\n `]\n})\n@RegisterClass(BaseArtifactViewerPluginComponent, 'DocxArtifactViewerPlugin')\nexport class DocxArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit {\n\n public isLoading = true;\n public isDownloading = false;\n public errorMessage = '';\n public safeHtml: SafeHtml = '';\n\n /** Raw HTML produced by mammoth — kept for print. */\n private rawHtml = '';\n private downloadUrl = '';\n\n constructor(\n private fileService: ArtifactFileService,\n private sanitizer: DomSanitizer,\n private cdr: ChangeDetectorRef,\n ) {\n super();\n }\n\n public override get hasDisplayContent(): boolean { return true; }\n\n async ngOnInit(): Promise<void> {\n await this.loadDocument();\n }\n\n public async onDownload(): Promise<void> {\n if (!this.downloadUrl || this.isDownloading) {\n return;\n }\n this.isDownloading = true;\n this.cdr.markForCheck();\n try {\n await this.triggerBrowserDownload(\n this.downloadUrl,\n this.artifactVersion?.FileName || 'document.docx',\n );\n } finally {\n this.isDownloading = false;\n this.cdr.markForCheck();\n }\n }\n\n public onPrint(): void {\n if (!this.rawHtml) {\n return;\n }\n this.openPrintWindow(this.rawHtml);\n }\n\n // ─── Private helpers ────────────────────────────────────────────────────────\n\n private async loadDocument(): Promise<void> {\n if (!this.artifactVersion?.ID) {\n this.showError('No artifact version provided.');\n return;\n }\n\n try {\n let arrayBuffer: ArrayBuffer;\n\n if (this.artifactVersion.ContentMode === 'File') {\n // File-backed: download from storage via pre-auth URL\n this.downloadUrl = await this.fileService.getDownloadUrl(this.artifactVersion.ID);\n arrayBuffer = await this.fetchAsArrayBuffer(this.downloadUrl);\n } else {\n // Inline: content is a base64 data URL stored in the artifact version\n const content = this.artifactVersion.Content;\n if (!content) {\n this.showError('Artifact has no content.');\n return;\n }\n arrayBuffer = this.fileService.dataUrlToArrayBuffer(content);\n // Create an object URL for download support\n this.downloadUrl = this.fileService.dataUrlToObjectUrl(\n content,\n this.artifactVersion.MimeType || 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'\n );\n }\n\n const html = await this.convertDocxToHtml(arrayBuffer);\n this.rawHtml = html;\n const sanitized = this.sanitizer.sanitize(SecurityContext.HTML, html) ?? '';\n this.safeHtml = sanitized;\n this.isLoading = false;\n this.cdr.markForCheck();\n } catch (err) {\n this.showError(`Could not load document: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n private async convertDocxToHtml(arrayBuffer: ArrayBuffer): Promise<string> {\n const mammoth = await import('mammoth');\n const result = await mammoth.convertToHtml({ arrayBuffer });\n if (result.messages?.length) {\n // Log warnings from mammoth (e.g. unsupported features) without breaking display\n result.messages.forEach(m => console.warn('[DocxViewer] mammoth:', m.message));\n }\n return result.value;\n }\n\n private async fetchAsArrayBuffer(url: string): Promise<ArrayBuffer> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} fetching file`);\n }\n return response.arrayBuffer();\n }\n\n /** Open a minimal print window containing only the document HTML. */\n private openPrintWindow(html: string): void {\n const printWindow = window.open('', '_blank', 'width=900,height=700');\n if (!printWindow) {\n console.warn('[DocxViewer] Popup was blocked — cannot print');\n return;\n }\n printWindow.document.write(`\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"utf-8\">\n <title>${this.artifactVersion?.FileName ?? 'Document'}</title>\n <style>\n body { font-family: Georgia, serif; font-size: 13pt; line-height: 1.6; margin: 2cm; color: #000; }\n table { border-collapse: collapse; width: 100%; }\n td, th { border: 1px solid #999; padding: 4px 8px; }\n th { background: #eee; font-weight: bold; }\n img { max-width: 100%; }\n </style>\n </head>\n <body>${html}</body>\n </html>\n `);\n printWindow.document.close();\n printWindow.focus();\n printWindow.print();\n printWindow.close();\n }\n\n private showError(message: string): void {\n this.isLoading = false;\n this.errorMessage = message;\n this.cdr.markForCheck();\n }\n\n}\n\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { OnInit, OnDestroy, ElementRef, ChangeDetectorRef } from '@angular/core';
|
|
2
|
+
import { BaseArtifactViewerPluginComponent } from '../base-artifact-viewer.component';
|
|
3
|
+
import { ArtifactFileService } from '../../services/artifact-file.service';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Viewer plugin for PDF artifact versions stored in MJStorage (ContentMode = 'File')
|
|
7
|
+
* or as inline base64 content (ContentMode = 'Inline').
|
|
8
|
+
*
|
|
9
|
+
* Renders one page at a time using PDF.js. The toolbar provides page navigation
|
|
10
|
+
* (including a direct page-jump input), zoom in/out, download, and print.
|
|
11
|
+
*
|
|
12
|
+
* Aspect-ratio preservation: the canvas backing buffer is sized at full HiDPI
|
|
13
|
+
* resolution while the CSS size uses `aspect-ratio` + `height: auto` so both
|
|
14
|
+
* dimensions scale proportionally. Pages wider than the panel scroll horizontally
|
|
15
|
+
* inside the body — no max-width clamping, which is required for zoom to work.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PdfArtifactViewerComponent extends BaseArtifactViewerPluginComponent implements OnInit, OnDestroy {
|
|
18
|
+
private fileService;
|
|
19
|
+
private cdr;
|
|
20
|
+
canvasRef?: ElementRef<HTMLCanvasElement>;
|
|
21
|
+
isLoading: boolean;
|
|
22
|
+
isDownloading: boolean;
|
|
23
|
+
errorMessage: string;
|
|
24
|
+
currentPage: number;
|
|
25
|
+
totalPages: number;
|
|
26
|
+
zoomLevel: number;
|
|
27
|
+
private pdfDoc;
|
|
28
|
+
private renderTask;
|
|
29
|
+
private downloadUrl;
|
|
30
|
+
private _pdfjsLib;
|
|
31
|
+
/** Discrete zoom steps — 50 % to 400 %. */
|
|
32
|
+
private readonly ZOOM_STEPS;
|
|
33
|
+
/** Base render scale (logical pixels per PDF point at zoom 1.0). */
|
|
34
|
+
private readonly BASE_SCALE;
|
|
35
|
+
constructor(fileService: ArtifactFileService, cdr: ChangeDetectorRef);
|
|
36
|
+
get hasDisplayContent(): boolean;
|
|
37
|
+
/** Current zoom as an integer percentage for toolbar display. */
|
|
38
|
+
get zoomPercent(): number;
|
|
39
|
+
get canZoomIn(): boolean;
|
|
40
|
+
get canZoomOut(): boolean;
|
|
41
|
+
ngOnInit(): Promise<void>;
|
|
42
|
+
ngOnDestroy(): void;
|
|
43
|
+
goToNextPage(): Promise<void>;
|
|
44
|
+
goToPrevPage(): Promise<void>;
|
|
45
|
+
onPageChange(page: number): Promise<void>;
|
|
46
|
+
onZoomIn(): Promise<void>;
|
|
47
|
+
onZoomOut(): Promise<void>;
|
|
48
|
+
onZoomReset(): Promise<void>;
|
|
49
|
+
onDownload(): Promise<void>;
|
|
50
|
+
onPrint(): void;
|
|
51
|
+
private loadPdf;
|
|
52
|
+
/**
|
|
53
|
+
* Set the initial zoom so the first page fills the available panel width.
|
|
54
|
+
* Uses the exact ratio rather than snapping to a ZOOM_STEP so the page fits
|
|
55
|
+
* pixel-perfectly. Subsequent +/- presses will snap to the nearest step.
|
|
56
|
+
*/
|
|
57
|
+
private setFitWidthZoom;
|
|
58
|
+
/** Dynamically import pdfjs-dist and point it at the bundled worker. */
|
|
59
|
+
private initPdfJs;
|
|
60
|
+
private loadPdfDocument;
|
|
61
|
+
private loadPdfFromData;
|
|
62
|
+
private renderPage;
|
|
63
|
+
private cancelCurrentRender;
|
|
64
|
+
private showError;
|
|
65
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PdfArtifactViewerComponent, never>;
|
|
66
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PdfArtifactViewerComponent, "mj-pdf-artifact-viewer", never, {}, {}, never, never, false, never>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=pdf-artifact-viewer.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdf-artifact-viewer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/plugins/pdf-artifact-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,SAAS,EAAa,UAAU,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvG,OAAO,EAAE,iCAAiC,EAAE,MAAM,mCAAmC,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;;AAE3E;;;;;;;;;;;GAWG;AACH,qBA8Fa,0BAA2B,SAAQ,iCAAkC,YAAW,MAAM,EAAE,SAAS;IAsB1G,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,GAAG;IArBW,SAAS,CAAC,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE3D,SAAS,UAAQ;IACjB,aAAa,UAAS;IACtB,YAAY,SAAM;IAClB,WAAW,SAAK;IAChB,UAAU,SAAK;IACf,SAAS,SAAO;IAEvB,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,SAAS,CAAyB;IAE1C,2CAA2C;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoD;IAC/E,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAO;gBAGxB,WAAW,EAAE,mBAAmB,EAChC,GAAG,EAAE,iBAAiB;IAKhC,IAAoB,iBAAiB,IAAI,OAAO,CAAiB;IAEjE,iEAAiE;IACjE,IAAW,WAAW,IAAI,MAAM,CAA6C;IAE7E,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,IAAW,UAAU,IAAI,OAAO,CAE/B;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,WAAW,IAAI,IAAI;IAQN,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAI7B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQzB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAU1B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAcjC,OAAO,IAAI,IAAI;YAUR,OAAO;IA4CrB;;;;OAIG;YACW,eAAe;IAkB7B,wEAAwE;YAC1D,SAAS;YAQT,eAAe;YAMf,eAAe;YAMf,UAAU;IAwCxB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,SAAS;yCAjQN,0BAA0B;2CAA1B,0BAA0B;CAuQtC"}
|