@nexterajs/report-viewer 0.1.0 → 0.1.1
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/NOTICE +16 -16
- package/README.md +98 -96
- package/fesm2022/nexterajs-report-viewer.mjs +1252 -597
- package/fesm2022/nexterajs-report-viewer.mjs.map +1 -1
- package/package.json +2 -2
- package/types/nexterajs-report-viewer.d.ts +63 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
1
|
+
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { input,
|
|
3
|
+
import { input, output, signal, viewChild, afterNextRender, ChangeDetectionStrategy, Component, computed, inject, DOCUMENT, ElementRef, Injectable, DestroyRef, linkedSignal } from '@angular/core';
|
|
4
|
+
import { defer, map, catchError, from, switchMap, throwError, timeout, Subject, Subscription, finalize, interval, exhaustMap, EMPTY, distinctUntilChanged, skip } from 'rxjs';
|
|
4
5
|
import { HttpErrorResponse, HttpClient } from '@angular/common/http';
|
|
5
|
-
import { defer, map, catchError, from, switchMap, throwError, timeout, Subject, Subscription, finalize, interval, exhaustMap, EMPTY } from 'rxjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The version of the viewer protocol this package speaks: the `protocol` member of every
|
|
@@ -10,6 +10,326 @@ import { defer, map, catchError, from, switchMap, throwError, timeout, Subject,
|
|
|
10
10
|
*/
|
|
11
11
|
const NXE_VIEWER_PROTOCOL = 1;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Finding text in the pages on screen, as Stimulsoft 2025.1.4's viewer does
|
|
15
|
+
* (angular-viewer-panels-chrome.md §1.4-1.7), over each page's shadow root.
|
|
16
|
+
*
|
|
17
|
+
* A candidate is an element whose only child is a single text node; text split across elements is
|
|
18
|
+
* never found. The element's HTML is compared, lower-cased unless the case must match, by a plain
|
|
19
|
+
* substring test; a whole word needs no ASCII letter or digit on either side of the first occurrence.
|
|
20
|
+
* Each hit gets one label, however often the text occurs in it.
|
|
21
|
+
*/
|
|
22
|
+
/** The label every hit carries, and how the current one differs from the rest. */
|
|
23
|
+
const LABEL_CLASS = 'nxe-find-label';
|
|
24
|
+
const CURRENT_BORDER = 'red';
|
|
25
|
+
const OTHER_BORDER = 'rgb(138, 138, 138)';
|
|
26
|
+
/** The inline position a hit had before its label made it a positioning box, to put back afterwards. */
|
|
27
|
+
const PREVIOUS_POSITION = 'nxeFindPosition';
|
|
28
|
+
const WORD_CHARACTER = /[A-Za-z0-9]/;
|
|
29
|
+
/** Every element in the roots, in document order, that holds the text. */
|
|
30
|
+
function findHits(roots, text, match) {
|
|
31
|
+
const needle = match.matchCase ? text : text.toLowerCase();
|
|
32
|
+
const hits = [];
|
|
33
|
+
if (needle === '')
|
|
34
|
+
return hits;
|
|
35
|
+
for (const root of roots) {
|
|
36
|
+
for (const element of Array.from(root.querySelectorAll('*'))) {
|
|
37
|
+
if (element.childNodes.length !== 1 || element.firstChild?.nodeType !== 3)
|
|
38
|
+
continue;
|
|
39
|
+
const html = match.matchCase ? element.innerHTML : element.innerHTML.toLowerCase();
|
|
40
|
+
const at = html.indexOf(needle);
|
|
41
|
+
if (at < 0)
|
|
42
|
+
continue;
|
|
43
|
+
if (match.wholeWord && !standsAlone(html, at, needle.length))
|
|
44
|
+
continue;
|
|
45
|
+
hits.push(element);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return hits;
|
|
49
|
+
}
|
|
50
|
+
function standsAlone(html, at, length) {
|
|
51
|
+
const before = at > 0 ? html[at - 1] : '';
|
|
52
|
+
const after = html[at + length] ?? '';
|
|
53
|
+
return !WORD_CHARACTER.test(before) && !WORD_CHARACTER.test(after);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Labels each hit: a yellow box over the element, 60 % opaque so the text reads through it, with a
|
|
57
|
+
* 2 px border that is red on the current hit and grey on the rest, inserted as the element's first
|
|
58
|
+
* child and sized to it. The element becomes the label's positioning box; Stimulsoft makes it
|
|
59
|
+
* `relative` unless it is positioned already, and it is here unless its computed position is
|
|
60
|
+
* `static`, because this engine positions its page elements from the stylesheet.
|
|
61
|
+
*/
|
|
62
|
+
function labelHits(hits, current) {
|
|
63
|
+
hits.forEach((element, index) => {
|
|
64
|
+
const label = element.ownerDocument.createElement('div');
|
|
65
|
+
label.className = LABEL_CLASS;
|
|
66
|
+
Object.assign(label.style, {
|
|
67
|
+
position: 'absolute',
|
|
68
|
+
left: '0px',
|
|
69
|
+
top: '0px',
|
|
70
|
+
width: `${Math.max(0, element.offsetWidth - 4)}px`,
|
|
71
|
+
height: `${Math.max(0, element.offsetHeight - 4)}px`,
|
|
72
|
+
zIndex: '150',
|
|
73
|
+
opacity: '0.6',
|
|
74
|
+
borderRadius: '3px',
|
|
75
|
+
background: 'rgb(255, 255, 0)',
|
|
76
|
+
border: `2px solid ${index === current ? CURRENT_BORDER : OTHER_BORDER}`,
|
|
77
|
+
pointerEvents: 'none',
|
|
78
|
+
});
|
|
79
|
+
if (getComputedStyle(element).position === 'static') {
|
|
80
|
+
element.dataset[PREVIOUS_POSITION] = element.style.position;
|
|
81
|
+
element.style.position = 'relative';
|
|
82
|
+
}
|
|
83
|
+
element.insertBefore(label, element.firstChild);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Moves the current mark from one hit to another. */
|
|
87
|
+
function markCurrent(hits, previous, current) {
|
|
88
|
+
const border = (index, color) => {
|
|
89
|
+
const label = hits[index]?.querySelector(`:scope > .${LABEL_CLASS}`);
|
|
90
|
+
if (label)
|
|
91
|
+
label.style.borderColor = color;
|
|
92
|
+
};
|
|
93
|
+
border(previous, OTHER_BORDER);
|
|
94
|
+
border(current, CURRENT_BORDER);
|
|
95
|
+
}
|
|
96
|
+
/** Removes every label from the roots, and gives back the position each hit had before it. */
|
|
97
|
+
function clearHits(roots) {
|
|
98
|
+
for (const root of roots) {
|
|
99
|
+
for (const label of Array.from(root.querySelectorAll(`.${LABEL_CLASS}`))) {
|
|
100
|
+
const element = label.parentElement;
|
|
101
|
+
label.remove();
|
|
102
|
+
if (element && PREVIOUS_POSITION in element.dataset) {
|
|
103
|
+
element.style.position = element.dataset[PREVIOUS_POSITION] ?? '';
|
|
104
|
+
delete element.dataset[PREVIOUS_POSITION];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Every caption and message the viewer shows, in its built-in English. A key in PascalCase is the name
|
|
112
|
+
* Stimulsoft's viewer gives the same text, such as `PageOf`; a key in camelCase names a message only this
|
|
113
|
+
* viewer has. The English is this package's own. A later plan lets a host's translation file replace any
|
|
114
|
+
* of them, so code reads them from an `NxeViewerStrings` value wherever one is at hand.
|
|
115
|
+
*/
|
|
116
|
+
const NXE_ENGLISH = {
|
|
117
|
+
/** The label before the page-number box. */
|
|
118
|
+
Page: 'Page',
|
|
119
|
+
/** The label between the page-number box and the page count. */
|
|
120
|
+
PageOf: 'of',
|
|
121
|
+
FirstPage: 'First page',
|
|
122
|
+
PrevPage: 'Previous page',
|
|
123
|
+
NextPage: 'Next page',
|
|
124
|
+
LastPage: 'Last page',
|
|
125
|
+
/** The save button's caption and tooltip, and its menu items, captioned by the format they write. */
|
|
126
|
+
Save: 'Save',
|
|
127
|
+
SaveToolTip: 'Save',
|
|
128
|
+
formatPdf: 'Adobe PDF',
|
|
129
|
+
formatHtml: 'HTML',
|
|
130
|
+
/** The resources button's tooltip. */
|
|
131
|
+
Resources: 'Resources',
|
|
132
|
+
/** The find button's tooltip, and the find panel's captions. */
|
|
133
|
+
FindToolTip: 'Find',
|
|
134
|
+
FindWhat: 'Find What',
|
|
135
|
+
FindPrevious: 'Find Previous',
|
|
136
|
+
FindNext: 'Find Next',
|
|
137
|
+
MatchCase: 'Match Case',
|
|
138
|
+
MatchWholeWord: 'Match Whole Word',
|
|
139
|
+
closeFind: 'Close',
|
|
140
|
+
/** The view-mode button's tooltip, and its menu items, whose captions are the mode names. */
|
|
141
|
+
ViewModeToolTip: 'View mode',
|
|
142
|
+
SinglePage: 'Single Page',
|
|
143
|
+
Continuous: 'Continuous',
|
|
144
|
+
MultiplePages: 'Multiple Pages',
|
|
145
|
+
/** The zoom button's tooltip; its caption is the current percentage, so it has no key. */
|
|
146
|
+
ZoomToolTip: 'Zoom',
|
|
147
|
+
/** Fitting a whole page, which Stimulsoft's menu captions by the page's height. */
|
|
148
|
+
ZoomOnePage: 'Page Height',
|
|
149
|
+
ZoomPageWidth: 'Page Width',
|
|
150
|
+
errorUnreachable: 'The report server could not be reached.',
|
|
151
|
+
errorStatus: 'The report server answered with HTTP status {0}.',
|
|
152
|
+
errorTimeout: 'The report server did not answer within {0} seconds.',
|
|
153
|
+
errorMalformed: "The report server's answer to {0} is not one this viewer can read.",
|
|
154
|
+
errorProtocol: 'The report server speaks viewer protocol {0}, and this viewer speaks protocol {1}.',
|
|
155
|
+
};
|
|
156
|
+
/** Puts each value in place of its numbered placeholder: `{0}` is the first value. */
|
|
157
|
+
function formatString(template, ...values) {
|
|
158
|
+
return template.replace(/\{(\d+)\}/g, (placeholder, index) => Number(index) < values.length ? String(values[Number(index)]) : placeholder);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The find panel, as Stimulsoft 2025.1.4 draws and runs it (angular-viewer-panels-chrome.md §1): a
|
|
163
|
+
* second bar below the toolbar with Close, the "Find What" box, Find Previous, Find Next, and the Match
|
|
164
|
+
* Case and Match Whole Word toggles, every item with a 2 px margin.
|
|
165
|
+
*
|
|
166
|
+
* It searches only on Enter in the box and on the two find buttons, never as the user types, and the
|
|
167
|
+
* toggles only change how the next search runs. It opens empty with the box focused, and has no
|
|
168
|
+
* tooltips. The viewer does the finding; this panel says what to find.
|
|
169
|
+
*/
|
|
170
|
+
class NxeViewerFindPanelComponent {
|
|
171
|
+
/** The captions to show. */
|
|
172
|
+
strings = input(NXE_ENGLISH, /* @ts-ignore */
|
|
173
|
+
...(ngDevMode ? [{ debugName: "strings" }] : /* istanbul ignore next */ []));
|
|
174
|
+
/** A search the user asked for. */
|
|
175
|
+
search = output();
|
|
176
|
+
/** The user closed the panel. */
|
|
177
|
+
closed = output();
|
|
178
|
+
matchCase = signal(false, /* @ts-ignore */
|
|
179
|
+
...(ngDevMode ? [{ debugName: "matchCase" }] : /* istanbul ignore next */ []));
|
|
180
|
+
wholeWord = signal(false, /* @ts-ignore */
|
|
181
|
+
...(ngDevMode ? [{ debugName: "wholeWord" }] : /* istanbul ignore next */ []));
|
|
182
|
+
box = viewChild.required('box', /* @ts-ignore */
|
|
183
|
+
...(ngDevMode ? [{ debugName: "box" }] : /* istanbul ignore next */ []));
|
|
184
|
+
constructor() {
|
|
185
|
+
// Stimulsoft focuses the box on the next tick after the panel opens.
|
|
186
|
+
afterNextRender(() => this.box().nativeElement.focus());
|
|
187
|
+
}
|
|
188
|
+
find(text, forward) {
|
|
189
|
+
this.search.emit({ text, forward, matchCase: this.matchCase(), wholeWord: this.wholeWord() });
|
|
190
|
+
}
|
|
191
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeViewerFindPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
192
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.5", type: NxeViewerFindPanelComponent, isStandalone: true, selector: "nxe-viewer-find-panel", inputs: { strings: { classPropertyName: "strings", publicName: "strings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", closed: "closed" }, host: { attributes: { "role": "search" }, classAttribute: "nxe-viewer-find-panel" }, viewQueries: [{ propertyName: "box", first: true, predicate: ["box"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
193
|
+
<div class="nxe-find-bar">
|
|
194
|
+
<button
|
|
195
|
+
type="button"
|
|
196
|
+
class="nxe-find-button"
|
|
197
|
+
[attr.aria-label]="strings().closeFind"
|
|
198
|
+
(click)="closed.emit()"
|
|
199
|
+
>
|
|
200
|
+
<span class="nxe-find-icon">
|
|
201
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
202
|
+
<path d="M3.5 3.5l9 9m0-9-9 9" fill="none" stroke="#707070" stroke-width="1.2" />
|
|
203
|
+
</svg>
|
|
204
|
+
</span>
|
|
205
|
+
</button>
|
|
206
|
+
<span class="nxe-find-label">{{ strings().FindWhat }}</span>
|
|
207
|
+
<input
|
|
208
|
+
#box
|
|
209
|
+
type="text"
|
|
210
|
+
class="nxe-find-box"
|
|
211
|
+
[attr.aria-label]="strings().FindWhat"
|
|
212
|
+
(keydown.enter)="find(box.value, true)"
|
|
213
|
+
/>
|
|
214
|
+
<button type="button" class="nxe-find-button" (click)="find(box.value, false)">
|
|
215
|
+
<span class="nxe-find-icon">
|
|
216
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
217
|
+
<path
|
|
218
|
+
d="M8 3.5 13 9h-3v3.5H6V9H3z"
|
|
219
|
+
fill="#81c1f1"
|
|
220
|
+
stroke="#0161b1"
|
|
221
|
+
stroke-linejoin="round"
|
|
222
|
+
/>
|
|
223
|
+
</svg>
|
|
224
|
+
</span>
|
|
225
|
+
<span class="nxe-find-caption">{{ strings().FindPrevious }}</span>
|
|
226
|
+
</button>
|
|
227
|
+
<button type="button" class="nxe-find-button" (click)="find(box.value, true)">
|
|
228
|
+
<span class="nxe-find-icon">
|
|
229
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
230
|
+
<path
|
|
231
|
+
d="M8 12.5 13 7h-3V3.5H6V7H3z"
|
|
232
|
+
fill="#81c1f1"
|
|
233
|
+
stroke="#0161b1"
|
|
234
|
+
stroke-linejoin="round"
|
|
235
|
+
/>
|
|
236
|
+
</svg>
|
|
237
|
+
</span>
|
|
238
|
+
<span class="nxe-find-caption">{{ strings().FindNext }}</span>
|
|
239
|
+
</button>
|
|
240
|
+
<button
|
|
241
|
+
type="button"
|
|
242
|
+
class="nxe-find-button"
|
|
243
|
+
[class.nxe-find-on]="matchCase()"
|
|
244
|
+
[attr.aria-pressed]="matchCase()"
|
|
245
|
+
(click)="matchCase.set(!matchCase())"
|
|
246
|
+
>
|
|
247
|
+
<span class="nxe-find-caption">{{ strings().MatchCase }}</span>
|
|
248
|
+
</button>
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
class="nxe-find-button"
|
|
252
|
+
[class.nxe-find-on]="wholeWord()"
|
|
253
|
+
[attr.aria-pressed]="wholeWord()"
|
|
254
|
+
(click)="wholeWord.set(!wholeWord())"
|
|
255
|
+
>
|
|
256
|
+
<span class="nxe-find-caption">{{ strings().MatchWholeWord }}</span>
|
|
257
|
+
</button>
|
|
258
|
+
</div>
|
|
259
|
+
`, isInline: true, styles: [":host{display:block;box-sizing:border-box;padding-top:2px;-webkit-user-select:none;user-select:none;font-family:Arial;font-size:12px;color:#202020}.nxe-find-bar{display:flex;align-items:center;box-sizing:border-box;height:31px;border:1px solid #c6c6c6;border-radius:3px;background:#fff;cursor:default}.nxe-find-button{display:inline-flex;align-items:center;box-sizing:border-box;height:25px;margin:2px;padding:0;border:1px solid transparent;border-radius:3px;background:transparent;color:inherit;font:inherit;white-space:nowrap;cursor:default}.nxe-find-button:hover{background:#dadada}.nxe-find-button.nxe-find-on{background:#e5e5e5}.nxe-find-icon{display:flex;flex:none;box-sizing:border-box;width:22px;padding-left:3px}.nxe-find-icon svg{width:16px;height:16px}.nxe-find-caption{padding:1px 5px 0 0}.nxe-find-label{margin:2px;padding:2px 0 0;white-space:nowrap}.nxe-find-box{box-sizing:content-box;width:170px;height:21px;margin:2px;padding:0 0 0 4px;border:1px solid #ababab;border-radius:3px;background:#fff;color:inherit;font:inherit;line-height:21px;outline:none}.nxe-find-box:hover{border-color:#6b6b6b}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
260
|
+
}
|
|
261
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeViewerFindPanelComponent, decorators: [{
|
|
262
|
+
type: Component,
|
|
263
|
+
args: [{ selector: 'nxe-viewer-find-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'nxe-viewer-find-panel', role: 'search' }, template: `
|
|
264
|
+
<div class="nxe-find-bar">
|
|
265
|
+
<button
|
|
266
|
+
type="button"
|
|
267
|
+
class="nxe-find-button"
|
|
268
|
+
[attr.aria-label]="strings().closeFind"
|
|
269
|
+
(click)="closed.emit()"
|
|
270
|
+
>
|
|
271
|
+
<span class="nxe-find-icon">
|
|
272
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
273
|
+
<path d="M3.5 3.5l9 9m0-9-9 9" fill="none" stroke="#707070" stroke-width="1.2" />
|
|
274
|
+
</svg>
|
|
275
|
+
</span>
|
|
276
|
+
</button>
|
|
277
|
+
<span class="nxe-find-label">{{ strings().FindWhat }}</span>
|
|
278
|
+
<input
|
|
279
|
+
#box
|
|
280
|
+
type="text"
|
|
281
|
+
class="nxe-find-box"
|
|
282
|
+
[attr.aria-label]="strings().FindWhat"
|
|
283
|
+
(keydown.enter)="find(box.value, true)"
|
|
284
|
+
/>
|
|
285
|
+
<button type="button" class="nxe-find-button" (click)="find(box.value, false)">
|
|
286
|
+
<span class="nxe-find-icon">
|
|
287
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
288
|
+
<path
|
|
289
|
+
d="M8 3.5 13 9h-3v3.5H6V9H3z"
|
|
290
|
+
fill="#81c1f1"
|
|
291
|
+
stroke="#0161b1"
|
|
292
|
+
stroke-linejoin="round"
|
|
293
|
+
/>
|
|
294
|
+
</svg>
|
|
295
|
+
</span>
|
|
296
|
+
<span class="nxe-find-caption">{{ strings().FindPrevious }}</span>
|
|
297
|
+
</button>
|
|
298
|
+
<button type="button" class="nxe-find-button" (click)="find(box.value, true)">
|
|
299
|
+
<span class="nxe-find-icon">
|
|
300
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
301
|
+
<path
|
|
302
|
+
d="M8 12.5 13 7h-3V3.5H6V7H3z"
|
|
303
|
+
fill="#81c1f1"
|
|
304
|
+
stroke="#0161b1"
|
|
305
|
+
stroke-linejoin="round"
|
|
306
|
+
/>
|
|
307
|
+
</svg>
|
|
308
|
+
</span>
|
|
309
|
+
<span class="nxe-find-caption">{{ strings().FindNext }}</span>
|
|
310
|
+
</button>
|
|
311
|
+
<button
|
|
312
|
+
type="button"
|
|
313
|
+
class="nxe-find-button"
|
|
314
|
+
[class.nxe-find-on]="matchCase()"
|
|
315
|
+
[attr.aria-pressed]="matchCase()"
|
|
316
|
+
(click)="matchCase.set(!matchCase())"
|
|
317
|
+
>
|
|
318
|
+
<span class="nxe-find-caption">{{ strings().MatchCase }}</span>
|
|
319
|
+
</button>
|
|
320
|
+
<button
|
|
321
|
+
type="button"
|
|
322
|
+
class="nxe-find-button"
|
|
323
|
+
[class.nxe-find-on]="wholeWord()"
|
|
324
|
+
[attr.aria-pressed]="wholeWord()"
|
|
325
|
+
(click)="wholeWord.set(!wholeWord())"
|
|
326
|
+
>
|
|
327
|
+
<span class="nxe-find-caption">{{ strings().MatchWholeWord }}</span>
|
|
328
|
+
</button>
|
|
329
|
+
</div>
|
|
330
|
+
`, styles: [":host{display:block;box-sizing:border-box;padding-top:2px;-webkit-user-select:none;user-select:none;font-family:Arial;font-size:12px;color:#202020}.nxe-find-bar{display:flex;align-items:center;box-sizing:border-box;height:31px;border:1px solid #c6c6c6;border-radius:3px;background:#fff;cursor:default}.nxe-find-button{display:inline-flex;align-items:center;box-sizing:border-box;height:25px;margin:2px;padding:0;border:1px solid transparent;border-radius:3px;background:transparent;color:inherit;font:inherit;white-space:nowrap;cursor:default}.nxe-find-button:hover{background:#dadada}.nxe-find-button.nxe-find-on{background:#e5e5e5}.nxe-find-icon{display:flex;flex:none;box-sizing:border-box;width:22px;padding-left:3px}.nxe-find-icon svg{width:16px;height:16px}.nxe-find-caption{padding:1px 5px 0 0}.nxe-find-label{margin:2px;padding:2px 0 0;white-space:nowrap}.nxe-find-box{box-sizing:content-box;width:170px;height:21px;margin:2px;padding:0 0 0 4px;border:1px solid #ababab;border-radius:3px;background:#fff;color:inherit;font:inherit;line-height:21px;outline:none}.nxe-find-box:hover{border-color:#6b6b6b}\n"] }]
|
|
331
|
+
}], ctorParameters: () => [], propDecorators: { strings: [{ type: i0.Input, args: [{ isSignal: true, alias: "strings", required: false }] }], search: [{ type: i0.Output, args: ["search"] }], closed: [{ type: i0.Output, args: ["closed"] }], box: [{ type: i0.ViewChild, args: ['box', { isSignal: true }] }] } });
|
|
332
|
+
|
|
13
333
|
/**
|
|
14
334
|
* Page HTML from the server, made safe to place in the application's document.
|
|
15
335
|
*
|
|
@@ -137,6 +457,9 @@ function adoptedSheet(cssText) {
|
|
|
137
457
|
return sheet;
|
|
138
458
|
}
|
|
139
459
|
|
|
460
|
+
/** CSS pixels per point, and the share of its height Stimulsoft's viewer gives a page. */
|
|
461
|
+
const PX_PER_PT = 96 / 72;
|
|
462
|
+
const VIEWER_HEIGHT = 239 / 240;
|
|
140
463
|
/**
|
|
141
464
|
* The viewer draws each page's frame and the space between pages, so the page CSS's own are cancelled.
|
|
142
465
|
*
|
|
@@ -171,9 +494,15 @@ class NxeReportPageComponent {
|
|
|
171
494
|
/** The zoom to draw the page at, as a factor: 1 is 100 %. */
|
|
172
495
|
zoom = input(1, /* @ts-ignore */
|
|
173
496
|
...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
174
|
-
|
|
497
|
+
/**
|
|
498
|
+
* The page's box in CSS pixels, as the server's page HTML draws it and Stimulsoft's viewer sizes a
|
|
499
|
+
* page: floor(W) wide and floor(H x 239/240) tall, 796 x 1118 for A4 (renderer-html's
|
|
500
|
+
* viewer-page.ts). Known from the page's size alone, so the frame holds its place before the HTML
|
|
501
|
+
* arrives, and scaled by the zoom.
|
|
502
|
+
*/
|
|
503
|
+
scaledWidth = computed(() => Math.floor(this.page().widthPoints * PX_PER_PT + 1e-9) * this.zoom(), /* @ts-ignore */
|
|
175
504
|
...(ngDevMode ? [{ debugName: "scaledWidth" }] : /* istanbul ignore next */ []));
|
|
176
|
-
scaledHeight = computed(() => this.page().heightPoints * this.zoom(), /* @ts-ignore */
|
|
505
|
+
scaledHeight = computed(() => Math.floor(this.page().heightPoints * PX_PER_PT * VIEWER_HEIGHT + 1e-9) * this.zoom(), /* @ts-ignore */
|
|
177
506
|
...(ngDevMode ? [{ debugName: "scaledHeight" }] : /* istanbul ignore next */ []));
|
|
178
507
|
document = inject(DOCUMENT);
|
|
179
508
|
root = inject(ElementRef).nativeElement.attachShadow({
|
|
@@ -194,56 +523,18 @@ class NxeReportPageComponent {
|
|
|
194
523
|
}
|
|
195
524
|
}
|
|
196
525
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeReportPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
197
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: NxeReportPageComponent, isStandalone: true, selector: "nxe-report-page", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: true, transformFunction: null }, html: { classPropertyName: "html", publicName: "html", isSignal: true, isRequired: false, transformFunction: null }, css: { classPropertyName: "css", publicName: "css", isSignal: true, isRequired: true, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-page": "page().pageNumber", "style.width.
|
|
526
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.5", type: NxeReportPageComponent, isStandalone: true, selector: "nxe-report-page", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: true, transformFunction: null }, html: { classPropertyName: "html", publicName: "html", isSignal: true, isRequired: false, transformFunction: null }, css: { classPropertyName: "css", publicName: "css", isSignal: true, isRequired: true, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-page": "page().pageNumber", "style.width.px": "scaledWidth()", "style.height.px": "scaledHeight()" }, classAttribute: "nxe-report-page" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;position:relative;box-sizing:content-box;contain:paint;background:#fff}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
198
527
|
}
|
|
199
528
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeReportPageComponent, decorators: [{
|
|
200
529
|
type: Component,
|
|
201
530
|
args: [{ selector: 'nxe-report-page', standalone: true, template: '', changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
202
531
|
class: 'nxe-report-page',
|
|
203
532
|
'[attr.data-page]': 'page().pageNumber',
|
|
204
|
-
'[style.width.
|
|
205
|
-
'[style.height.
|
|
533
|
+
'[style.width.px]': 'scaledWidth()',
|
|
534
|
+
'[style.height.px]': 'scaledHeight()',
|
|
206
535
|
}, styles: [":host{display:block;position:relative;box-sizing:content-box;contain:paint;background:#fff}\n"] }]
|
|
207
536
|
}], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: true }] }], html: [{ type: i0.Input, args: [{ isSignal: true, alias: "html", required: false }] }], css: [{ type: i0.Input, args: [{ isSignal: true, alias: "css", required: true }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }] } });
|
|
208
537
|
|
|
209
|
-
/**
|
|
210
|
-
* Every caption and message the viewer shows, in its built-in English. A key in PascalCase is the name
|
|
211
|
-
* Stimulsoft's viewer gives the same text, such as `PageOf`; a key in camelCase names a message only this
|
|
212
|
-
* viewer has. The English is this package's own. A later plan lets a host's translation file replace any
|
|
213
|
-
* of them, so code reads them from an `NxeViewerStrings` value wherever one is at hand.
|
|
214
|
-
*/
|
|
215
|
-
const NXE_ENGLISH = {
|
|
216
|
-
/** The label before the page-number box. */
|
|
217
|
-
Page: 'Page',
|
|
218
|
-
/** The label between the page-number box and the page count. */
|
|
219
|
-
PageOf: 'of',
|
|
220
|
-
FirstPage: 'First page',
|
|
221
|
-
PrevPage: 'Previous page',
|
|
222
|
-
NextPage: 'Next page',
|
|
223
|
-
LastPage: 'Last page',
|
|
224
|
-
/** The save button's caption and tooltip; its menu items are captioned by the format they write. */
|
|
225
|
-
Save: 'Save',
|
|
226
|
-
SaveToolTip: 'Save',
|
|
227
|
-
/** The view-mode button's tooltip, and its menu items, whose captions are the mode names. */
|
|
228
|
-
ViewModeToolTip: 'View mode',
|
|
229
|
-
SinglePage: 'Single Page',
|
|
230
|
-
Continuous: 'Continuous',
|
|
231
|
-
MultiplePages: 'Multiple Pages',
|
|
232
|
-
/** The zoom button's tooltip; its caption is the current percentage, so it has no key. */
|
|
233
|
-
ZoomToolTip: 'Zoom',
|
|
234
|
-
ZoomOnePage: 'One Page',
|
|
235
|
-
ZoomPageWidth: 'Page Width',
|
|
236
|
-
errorUnreachable: 'The report server could not be reached.',
|
|
237
|
-
errorStatus: 'The report server answered with HTTP status {0}.',
|
|
238
|
-
errorTimeout: 'The report server did not answer within {0} seconds.',
|
|
239
|
-
errorMalformed: "The report server's answer to {0} is not one this viewer can read.",
|
|
240
|
-
errorProtocol: 'The report server speaks viewer protocol {0}, and this viewer speaks protocol {1}.',
|
|
241
|
-
};
|
|
242
|
-
/** Puts each value in place of its numbered placeholder: `{0}` is the first value. */
|
|
243
|
-
function formatString(template, ...values) {
|
|
244
|
-
return template.replace(/\{(\d+)\}/g, (placeholder, index) => Number(index) < values.length ? String(values[Number(index)]) : placeholder);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
538
|
/**
|
|
248
539
|
* A success answer that is not what the protocol says its action answers: the wrong shape, or an
|
|
249
540
|
* AngularViewerData answer from another version of the protocol.
|
|
@@ -813,25 +1104,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
|
|
|
813
1104
|
type: Injectable
|
|
814
1105
|
}], ctorParameters: () => [] });
|
|
815
1106
|
|
|
816
|
-
/** The width of the page-number box, as Stimulsoft's page control sizes it. */
|
|
817
|
-
const PAGE_BOX_WIDTH_PX = 45;
|
|
818
1107
|
/** The zoom menu's fixed percentages, in Stimulsoft's order, above the two fit items. */
|
|
819
1108
|
const ZOOM_PRESETS = [25, 50, 75, 100, 150, 200];
|
|
820
1109
|
/** The view-mode menu's items, in Stimulsoft's order. */
|
|
821
1110
|
const VIEW_MODES = ['SinglePage', 'Continuous', 'MultiplePages'];
|
|
822
1111
|
/**
|
|
823
|
-
* The toolbar
|
|
1112
|
+
* The toolbar, as Stimulsoft 2025.1.4 draws it in its default theme, Office2022WhiteBlue.
|
|
1113
|
+
*
|
|
1114
|
+
* The items and the rules for each are Stimulsoft's (angular-viewer-toolbar-view.md §1): Save and
|
|
1115
|
+
* Resources, Find, the page navigation and the page control, then Zoom and View mode, each group
|
|
1116
|
+
* opened by a separator whose condition is the group's own. Buttons for what this viewer does not do
|
|
1117
|
+
* yet — Print, Open, Send Email, Bookmarks, Parameters, Editor, Full Screen — are left out; bright and
|
|
1118
|
+
* orbit turn all of them off.
|
|
824
1119
|
*
|
|
825
|
-
* Stimulsoft's
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
1120
|
+
* The look was measured on Stimulsoft's own viewer, side by side with this one: a 31 px bar with a 1 px
|
|
1121
|
+
* #c6c6c6 border and 3 px corners, 12 px Arial in #202020; 24 x 25 buttons with a transparent 1 px
|
|
1122
|
+
* border, #dadada under the mouse or while their menu is open, #e5e5e5 while selected, and a half
|
|
1123
|
+
* transparent icon when disabled; 1 x 21 separators in #d4d4d4; menus with a 1 px #ababab border, a
|
|
1124
|
+
* 7 px shadow and 24 px items. The icons are this package's own drawings in the same colours.
|
|
830
1125
|
*
|
|
831
1126
|
* Navigation is disabled the way Stimulsoft disables it: in `MultiplePages`, and in `Continuous`
|
|
832
|
-
* unless the report panel or the whole viewer scrolls. A button is disabled at the end of the
|
|
833
|
-
*
|
|
834
|
-
* its way.
|
|
1127
|
+
* unless the report panel or the whole viewer scrolls. A button is disabled at the end of the report it
|
|
1128
|
+
* would move past, and everything is disabled while a request the user waits for is on its way.
|
|
835
1129
|
*/
|
|
836
1130
|
class NxeViewerToolbarComponent {
|
|
837
1131
|
/** The options from the server, or null before the first answer, when the toolbar shows nothing. */
|
|
@@ -855,6 +1149,9 @@ class NxeViewerToolbarComponent {
|
|
|
855
1149
|
/** The zoom the pages are drawn at, as a factor: 1 is 100 %. */
|
|
856
1150
|
zoom = input(1, /* @ts-ignore */
|
|
857
1151
|
...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
|
|
1152
|
+
/** Whether the find panel is open, which Stimulsoft's Find button shows as its selected state. */
|
|
1153
|
+
findOpen = input(false, /* @ts-ignore */
|
|
1154
|
+
...(ngDevMode ? [{ debugName: "findOpen" }] : /* istanbul ignore next */ []));
|
|
858
1155
|
/** The 0-based page the user asked for. */
|
|
859
1156
|
goToPage = output();
|
|
860
1157
|
/** The mode the user chose. */
|
|
@@ -865,7 +1162,8 @@ class NxeViewerToolbarComponent {
|
|
|
865
1162
|
fitRequest = output();
|
|
866
1163
|
/** The format the user asked the report to be saved as. */
|
|
867
1164
|
saveRequest = output();
|
|
868
|
-
|
|
1165
|
+
/** The user pressed Find, which opens the find panel or closes it. */
|
|
1166
|
+
findToggle = output();
|
|
869
1167
|
zoomPresets = ZOOM_PRESETS;
|
|
870
1168
|
viewModes = VIEW_MODES;
|
|
871
1169
|
/** Which menu is open, or null. Stimulsoft opens one at a time and closes it on the next click. */
|
|
@@ -890,6 +1188,10 @@ class NxeViewerToolbarComponent {
|
|
|
890
1188
|
...(ngDevMode ? [{ debugName: "saveFormats" }] : /* istanbul ignore next */ []));
|
|
891
1189
|
showSave = computed(() => this.toolbar()?.showSaveButton === true && this.saveFormats().length > 0, /* @ts-ignore */
|
|
892
1190
|
...(ngDevMode ? [{ debugName: "showSave" }] : /* istanbul ignore next */ []));
|
|
1191
|
+
/** The caption Stimulsoft's save menu gives a format. */
|
|
1192
|
+
formatCaption(format) {
|
|
1193
|
+
return format === 'Pdf' ? this.strings().formatPdf : this.strings().formatHtml;
|
|
1194
|
+
}
|
|
893
1195
|
save(format) {
|
|
894
1196
|
this.openMenuState.set(null);
|
|
895
1197
|
this.saveRequest.emit(format);
|
|
@@ -923,6 +1225,13 @@ class NxeViewerToolbarComponent {
|
|
|
923
1225
|
!appearance.fullScreenMode);
|
|
924
1226
|
}, /* @ts-ignore */
|
|
925
1227
|
...(ngDevMode ? [{ debugName: "navigationDisabled" }] : /* istanbul ignore next */ []));
|
|
1228
|
+
/** Save, Zoom and View mode show their captions unless the server turns captions off. */
|
|
1229
|
+
showCaptions = computed(() => this.toolbar()?.showButtonCaptions !== false, /* @ts-ignore */
|
|
1230
|
+
...(ngDevMode ? [{ debugName: "showCaptions" }] : /* istanbul ignore next */ []));
|
|
1231
|
+
showResources = computed(() => this.toolbar()?.showResourcesButton === true, /* @ts-ignore */
|
|
1232
|
+
...(ngDevMode ? [{ debugName: "showResources" }] : /* istanbul ignore next */ []));
|
|
1233
|
+
showFind = computed(() => this.toolbar()?.showFindButton === true, /* @ts-ignore */
|
|
1234
|
+
...(ngDevMode ? [{ debugName: "showFind" }] : /* istanbul ignore next */ []));
|
|
926
1235
|
showFirstPage = computed(() => this.toolbar()?.showFirstPageButton === true, /* @ts-ignore */
|
|
927
1236
|
...(ngDevMode ? [{ debugName: "showFirstPage" }] : /* istanbul ignore next */ []));
|
|
928
1237
|
showPreviousPage = computed(() => this.toolbar()?.showPreviousPageButton === true, /* @ts-ignore */
|
|
@@ -937,6 +1246,22 @@ class NxeViewerToolbarComponent {
|
|
|
937
1246
|
...(ngDevMode ? [{ debugName: "showZoom" }] : /* istanbul ignore next */ []));
|
|
938
1247
|
showViewMode = computed(() => this.toolbar()?.showViewModeButton === true, /* @ts-ignore */
|
|
939
1248
|
...(ngDevMode ? [{ debugName: "showViewMode" }] : /* istanbul ignore next */ []));
|
|
1249
|
+
/**
|
|
1250
|
+
* Stimulsoft opens the panels group — Bookmarks, Parameters, Resources — only when Bookmarks or
|
|
1251
|
+
* Parameters is on; Resources alone follows Save with no separator, as bright and orbit show it.
|
|
1252
|
+
*/
|
|
1253
|
+
showPanelsSeparator = computed(() => this.toolbar()?.showBookmarksButton === true || this.toolbar()?.showParametersButton === true, /* @ts-ignore */
|
|
1254
|
+
...(ngDevMode ? [{ debugName: "showPanelsSeparator" }] : /* istanbul ignore next */ []));
|
|
1255
|
+
/** Stimulsoft opens the Find group whenever Find or Editor is on, whatever comes before it. */
|
|
1256
|
+
showFindSeparator = computed(() => this.toolbar()?.showFindButton === true || this.toolbar()?.showEditorButton === true, /* @ts-ignore */
|
|
1257
|
+
...(ngDevMode ? [{ debugName: "showFindSeparator" }] : /* istanbul ignore next */ []));
|
|
1258
|
+
/** And the navigation group whenever any of its five items is on. */
|
|
1259
|
+
showNavigationSeparator = computed(() => this.showFirstPage() ||
|
|
1260
|
+
this.showPreviousPage() ||
|
|
1261
|
+
this.showCurrentPage() ||
|
|
1262
|
+
this.showNextPage() ||
|
|
1263
|
+
this.showLastPage(), /* @ts-ignore */
|
|
1264
|
+
...(ngDevMode ? [{ debugName: "showNavigationSeparator" }] : /* istanbul ignore next */ []));
|
|
940
1265
|
/** Whether a preset is the zoom on show, so its menu item reads as chosen. */
|
|
941
1266
|
isZoom(percent) {
|
|
942
1267
|
return Math.round(this.zoom() * 100) === percent;
|
|
@@ -969,12 +1294,17 @@ class NxeViewerToolbarComponent {
|
|
|
969
1294
|
...(ngDevMode ? [{ debugName: "fontColor" }] : /* istanbul ignore next */ []));
|
|
970
1295
|
fontFamily = computed(() => this.toolbar()?.fontFamily || null, /* @ts-ignore */
|
|
971
1296
|
...(ngDevMode ? [{ debugName: "fontFamily" }] : /* istanbul ignore next */ []));
|
|
972
|
-
/**
|
|
1297
|
+
/**
|
|
1298
|
+
* The server's border colour in `Simple`, and none at all in `Separated`, as Stimulsoft's toolbar
|
|
1299
|
+
* does; without a colour the theme's own border stays.
|
|
1300
|
+
*/
|
|
973
1301
|
border = computed(() => {
|
|
974
1302
|
const toolbar = this.toolbar();
|
|
975
|
-
if (!toolbar
|
|
1303
|
+
if (!toolbar)
|
|
976
1304
|
return null;
|
|
977
|
-
|
|
1305
|
+
if (toolbar.displayMode === 'Separated')
|
|
1306
|
+
return '0';
|
|
1307
|
+
return toolbar.borderColor ? `1px solid ${toolbar.borderColor}` : null;
|
|
978
1308
|
}, /* @ts-ignore */
|
|
979
1309
|
...(ngDevMode ? [{ debugName: "border" }] : /* istanbul ignore next */ []));
|
|
980
1310
|
justifyContent = computed(() => {
|
|
@@ -1005,435 +1335,602 @@ class NxeViewerToolbarComponent {
|
|
|
1005
1335
|
this.goToPage.emit(index);
|
|
1006
1336
|
}
|
|
1007
1337
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeViewerToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1008
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeViewerToolbarComponent, isStandalone: true, selector: "nxe-viewer-toolbar", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, pageCount: { classPropertyName: "pageCount", publicName: "pageCount", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, strings: { classPropertyName: "strings", publicName: "strings", isSignal: true, isRequired: false, transformFunction: null }, viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { goToPage: "goToPage", viewModeChange: "viewModeChange", zoomChange: "zoomChange", fitRequest: "fitRequest", saveRequest: "saveRequest" }, host: { attributes: { "role": "toolbar" }, properties: { "style.
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
[
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
<
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
<button
|
|
1169
|
-
type="button"
|
|
1170
|
-
class="nxe-toolbar-button
|
|
1171
|
-
[
|
|
1172
|
-
[attr.aria-
|
|
1173
|
-
(click)="
|
|
1174
|
-
>
|
|
1175
|
-
<
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1338
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeViewerToolbarComponent, isStandalone: true, selector: "nxe-viewer-toolbar", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, pageIndex: { classPropertyName: "pageIndex", publicName: "pageIndex", isSignal: true, isRequired: false, transformFunction: null }, pageCount: { classPropertyName: "pageCount", publicName: "pageCount", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, strings: { classPropertyName: "strings", publicName: "strings", isSignal: true, isRequired: false, transformFunction: null }, viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, findOpen: { classPropertyName: "findOpen", publicName: "findOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { goToPage: "goToPage", viewModeChange: "viewModeChange", zoomChange: "zoomChange", fitRequest: "fitRequest", saveRequest: "saveRequest", findToggle: "findToggle" }, host: { attributes: { "role": "toolbar" }, properties: { "style.color": "fontColor()", "style.font-family": "fontFamily()", "class.nxe-busy": "busy()" }, classAttribute: "nxe-viewer-toolbar" }, ngImport: i0, template: `
|
|
1339
|
+
<div
|
|
1340
|
+
class="nxe-toolbar-bar"
|
|
1341
|
+
[style.background-color]="backgroundColor()"
|
|
1342
|
+
[style.border]="border()"
|
|
1343
|
+
[style.justify-content]="justifyContent()"
|
|
1344
|
+
>
|
|
1345
|
+
@if (showSave()) {
|
|
1346
|
+
<div class="nxe-toolbar-menu">
|
|
1347
|
+
<button
|
|
1348
|
+
type="button"
|
|
1349
|
+
class="nxe-toolbar-button"
|
|
1350
|
+
[attr.aria-label]="strings().SaveToolTip"
|
|
1351
|
+
[attr.aria-expanded]="openMenu() === 'save'"
|
|
1352
|
+
(click)="toggleMenu('save')"
|
|
1353
|
+
>
|
|
1354
|
+
<span class="nxe-toolbar-icon">
|
|
1355
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
1356
|
+
<path d="M1.5 1.5h13v13h-13z" fill="#d890d8" stroke="#903098" />
|
|
1357
|
+
<path d="M4.5 1.5h7v4.5h-7z" fill="#fff" stroke="#903098" />
|
|
1358
|
+
<path d="M3.5 9.5h9v5h-9z" fill="#fff" stroke="#903098" />
|
|
1359
|
+
</svg>
|
|
1360
|
+
</span>
|
|
1361
|
+
@if (showCaptions()) {
|
|
1362
|
+
<span class="nxe-toolbar-caption">{{ strings().Save }}</span>
|
|
1363
|
+
}
|
|
1364
|
+
<span class="nxe-toolbar-arrow">
|
|
1365
|
+
<svg viewBox="0 0 8 8" aria-hidden="true">
|
|
1366
|
+
<path d="M1.5 2.5 4 5l2.5-2.5" fill="none" stroke="#787878" />
|
|
1367
|
+
</svg>
|
|
1368
|
+
</span>
|
|
1369
|
+
</button>
|
|
1370
|
+
@if (openMenu() === 'save') {
|
|
1371
|
+
<div class="nxe-toolbar-popup" role="menu">
|
|
1372
|
+
@for (format of saveFormats(); track format) {
|
|
1373
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="save(format)">
|
|
1374
|
+
<span class="nxe-toolbar-item-icon">
|
|
1375
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
1376
|
+
<path d="M3.5.5h7l3 3v12h-10z" fill="#fff" stroke="#6e6e6e" />
|
|
1377
|
+
<path d="M10.5.5v3h3" fill="none" stroke="#6e6e6e" />
|
|
1378
|
+
@if (format === 'Pdf') {
|
|
1379
|
+
<path d="M.5 6.5h11v5H.5z" fill="#f6d0d0" stroke="#d83b3b" />
|
|
1380
|
+
<text x="6" y="10.4" fill="#d83b3b" font-family="Arial" font-size="4.2" font-weight="bold" text-anchor="middle">PDF</text>
|
|
1381
|
+
} @else {
|
|
1382
|
+
<path d="M.5 6.5h13v5H.5z" fill="#d6e9f8" stroke="#2088d0" />
|
|
1383
|
+
<text x="7" y="10.4" fill="#2088d0" font-family="Arial" font-size="4" font-weight="bold" text-anchor="middle">HTML</text>
|
|
1384
|
+
}
|
|
1385
|
+
</svg>
|
|
1386
|
+
</span>
|
|
1387
|
+
<span class="nxe-toolbar-item-text">{{ formatCaption(format) }}</span>
|
|
1388
|
+
</button>
|
|
1389
|
+
}
|
|
1390
|
+
</div>
|
|
1391
|
+
}
|
|
1392
|
+
</div>
|
|
1393
|
+
}
|
|
1394
|
+
@if (showPanelsSeparator()) {
|
|
1395
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1396
|
+
}
|
|
1397
|
+
@if (showResources()) {
|
|
1398
|
+
<!-- A report's resources are not served yet, so there are none to show: Stimulsoft disables the button then. -->
|
|
1399
|
+
<button type="button" class="nxe-toolbar-button" disabled [attr.aria-label]="strings().Resources">
|
|
1400
|
+
<span class="nxe-toolbar-icon">
|
|
1401
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="#313131">
|
|
1402
|
+
<path d="M6.5 1.5h5l3 3v8h-8z" fill="#fff" />
|
|
1403
|
+
<path d="M11.5 1.5v3h3" />
|
|
1404
|
+
<path d="M1.5 4.5h5v10h-5z" fill="#fff" />
|
|
1405
|
+
<path d="M2.5 7.5h3m-3 2h3m-3 2h3" stroke="#717171" />
|
|
1406
|
+
</svg>
|
|
1407
|
+
</span>
|
|
1408
|
+
</button>
|
|
1409
|
+
}
|
|
1410
|
+
@if (showFindSeparator()) {
|
|
1411
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1412
|
+
}
|
|
1413
|
+
@if (showFind()) {
|
|
1414
|
+
<button
|
|
1415
|
+
type="button"
|
|
1416
|
+
class="nxe-toolbar-button"
|
|
1417
|
+
[class.nxe-toolbar-selected]="findOpen()"
|
|
1418
|
+
[attr.aria-label]="strings().FindToolTip"
|
|
1419
|
+
[attr.aria-pressed]="findOpen()"
|
|
1420
|
+
(click)="findToggle.emit()"
|
|
1421
|
+
>
|
|
1422
|
+
<span class="nxe-toolbar-icon">
|
|
1423
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="#383838">
|
|
1424
|
+
<circle cx="9.5" cy="6.5" r="4.4" stroke-width="1.4" />
|
|
1425
|
+
<path d="M6.4 9.6 2 14" stroke-width="2" stroke-linecap="round" />
|
|
1426
|
+
</svg>
|
|
1427
|
+
</span>
|
|
1428
|
+
</button>
|
|
1429
|
+
}
|
|
1430
|
+
@if (showNavigationSeparator()) {
|
|
1431
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1432
|
+
}
|
|
1433
|
+
@if (showFirstPage()) {
|
|
1434
|
+
<button
|
|
1435
|
+
type="button"
|
|
1436
|
+
class="nxe-toolbar-button"
|
|
1437
|
+
[disabled]="atStart()"
|
|
1438
|
+
[attr.aria-label]="strings().FirstPage"
|
|
1439
|
+
(click)="goTo(0)"
|
|
1440
|
+
>
|
|
1441
|
+
<span class="nxe-toolbar-icon">
|
|
1442
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1443
|
+
<path d="M3.5 2.5h2v11h-2z" />
|
|
1444
|
+
<path d="M12.5 2.5v11l-6-5.5z" />
|
|
1445
|
+
</svg>
|
|
1446
|
+
</span>
|
|
1447
|
+
</button>
|
|
1448
|
+
}
|
|
1449
|
+
@if (showPreviousPage()) {
|
|
1450
|
+
<button
|
|
1451
|
+
type="button"
|
|
1452
|
+
class="nxe-toolbar-button"
|
|
1453
|
+
[disabled]="atStart()"
|
|
1454
|
+
[attr.aria-label]="strings().PrevPage"
|
|
1455
|
+
(click)="goTo(pageIndex() - 1)"
|
|
1456
|
+
>
|
|
1457
|
+
<span class="nxe-toolbar-icon">
|
|
1458
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1459
|
+
<path d="M10.5 2.5v11l-6-5.5z" />
|
|
1460
|
+
</svg>
|
|
1461
|
+
</span>
|
|
1462
|
+
</button>
|
|
1463
|
+
}
|
|
1464
|
+
@if (showCurrentPage()) {
|
|
1465
|
+
<span class="nxe-page-control">
|
|
1466
|
+
<span class="nxe-page-label">{{ strings().Page }}</span>
|
|
1467
|
+
<span class="nxe-page-box-cell" [class.nxe-page-box-off]="pageControlDisabled()">
|
|
1468
|
+
<input
|
|
1469
|
+
type="text"
|
|
1470
|
+
inputmode="numeric"
|
|
1471
|
+
class="nxe-page-box"
|
|
1472
|
+
[disabled]="pageControlDisabled()"
|
|
1473
|
+
[value]="pageIndex() + 1"
|
|
1474
|
+
[attr.aria-label]="strings().Page"
|
|
1475
|
+
(keydown.enter)="enterPage($event)"
|
|
1476
|
+
/>
|
|
1477
|
+
</span>
|
|
1478
|
+
<span class="nxe-page-label">{{ strings().PageOf }}</span>
|
|
1479
|
+
<span class="nxe-page-label">{{ pageCount() }}</span>
|
|
1480
|
+
</span>
|
|
1481
|
+
}
|
|
1482
|
+
@if (showNextPage()) {
|
|
1483
|
+
<button
|
|
1484
|
+
type="button"
|
|
1485
|
+
class="nxe-toolbar-button"
|
|
1486
|
+
[disabled]="atEnd()"
|
|
1487
|
+
[attr.aria-label]="strings().NextPage"
|
|
1488
|
+
(click)="goTo(pageIndex() + 1)"
|
|
1489
|
+
>
|
|
1490
|
+
<span class="nxe-toolbar-icon">
|
|
1491
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1492
|
+
<path d="M5.5 2.5v11l6-5.5z" />
|
|
1493
|
+
</svg>
|
|
1494
|
+
</span>
|
|
1495
|
+
</button>
|
|
1496
|
+
}
|
|
1497
|
+
@if (showLastPage()) {
|
|
1498
|
+
<button
|
|
1499
|
+
type="button"
|
|
1500
|
+
class="nxe-toolbar-button"
|
|
1501
|
+
[disabled]="atEnd()"
|
|
1502
|
+
[attr.aria-label]="strings().LastPage"
|
|
1503
|
+
(click)="goTo(pageCount() - 1)"
|
|
1504
|
+
>
|
|
1505
|
+
<span class="nxe-toolbar-icon">
|
|
1506
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1507
|
+
<path d="M3.5 2.5v11l6-5.5z" />
|
|
1508
|
+
<path d="M10.5 2.5h2v11h-2z" />
|
|
1509
|
+
</svg>
|
|
1510
|
+
</span>
|
|
1511
|
+
</button>
|
|
1512
|
+
}
|
|
1513
|
+
@if (showZoom() || showViewMode()) {
|
|
1514
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1515
|
+
}
|
|
1516
|
+
@if (showZoom()) {
|
|
1517
|
+
<div class="nxe-toolbar-menu">
|
|
1518
|
+
<button
|
|
1519
|
+
type="button"
|
|
1520
|
+
class="nxe-toolbar-button"
|
|
1521
|
+
[attr.aria-label]="strings().ZoomToolTip"
|
|
1522
|
+
[attr.aria-expanded]="openMenu() === 'zoom'"
|
|
1523
|
+
(click)="toggleMenu('zoom')"
|
|
1524
|
+
>
|
|
1525
|
+
<span class="nxe-toolbar-icon">
|
|
1526
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
1527
|
+
<path d="M2.5 1.5h6l3 3v5M8.5 1.5v3h3M2.5 1.5v13h7" stroke="#383838" />
|
|
1528
|
+
<circle cx="11.5" cy="11.5" r="2" stroke="#2088d0" />
|
|
1529
|
+
<path d="m13 13 1.8 1.8" stroke="#2088d0" />
|
|
1530
|
+
</svg>
|
|
1531
|
+
</span>
|
|
1532
|
+
@if (showCaptions()) {
|
|
1533
|
+
<span class="nxe-toolbar-caption">{{ zoomCaption() }}</span>
|
|
1534
|
+
}
|
|
1535
|
+
<span class="nxe-toolbar-arrow">
|
|
1536
|
+
<svg viewBox="0 0 8 8" aria-hidden="true">
|
|
1537
|
+
<path d="M1.5 2.5 4 5l2.5-2.5" fill="none" stroke="#787878" />
|
|
1538
|
+
</svg>
|
|
1539
|
+
</span>
|
|
1540
|
+
</button>
|
|
1541
|
+
@if (openMenu() === 'zoom') {
|
|
1542
|
+
<div class="nxe-toolbar-popup" role="menu">
|
|
1543
|
+
@for (percent of zoomPresets; track percent) {
|
|
1544
|
+
<button
|
|
1545
|
+
type="button"
|
|
1546
|
+
class="nxe-toolbar-item"
|
|
1547
|
+
role="menuitem"
|
|
1548
|
+
[class.nxe-toolbar-item-on]="isZoom(percent)"
|
|
1549
|
+
(click)="chooseZoom(percent)"
|
|
1550
|
+
>
|
|
1551
|
+
<span class="nxe-toolbar-item-icon"></span>
|
|
1552
|
+
<span class="nxe-toolbar-item-text">{{ percent }}%</span>
|
|
1553
|
+
</button>
|
|
1554
|
+
}
|
|
1555
|
+
<span class="nxe-toolbar-item-separator"></span>
|
|
1556
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="fit('page')">
|
|
1557
|
+
<span class="nxe-toolbar-item-icon">
|
|
1558
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
1559
|
+
<path d="M3.5 1.5h9v13h-9z" fill="#fff" stroke="#383838" />
|
|
1560
|
+
<path d="M8 3.5v9M6 5.5l2-2 2 2M6 10.5l2 2 2-2" stroke="#2088d0" />
|
|
1561
|
+
</svg>
|
|
1562
|
+
</span>
|
|
1563
|
+
<span class="nxe-toolbar-item-text">{{ strings().ZoomOnePage }}</span>
|
|
1564
|
+
</button>
|
|
1565
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="fit('width')">
|
|
1566
|
+
<span class="nxe-toolbar-item-icon">
|
|
1567
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
1568
|
+
<path d="M1.5 3.5h13v9h-13z" fill="#fff" stroke="#383838" />
|
|
1569
|
+
<path d="M3.5 8h9M5.5 6l-2 2 2 2M10.5 6l2 2-2 2" stroke="#2088d0" />
|
|
1570
|
+
</svg>
|
|
1571
|
+
</span>
|
|
1572
|
+
<span class="nxe-toolbar-item-text">{{ strings().ZoomPageWidth }}</span>
|
|
1573
|
+
</button>
|
|
1574
|
+
</div>
|
|
1575
|
+
}
|
|
1576
|
+
</div>
|
|
1577
|
+
}
|
|
1578
|
+
@if (showViewMode()) {
|
|
1579
|
+
<div class="nxe-toolbar-menu">
|
|
1580
|
+
<button
|
|
1581
|
+
type="button"
|
|
1582
|
+
class="nxe-toolbar-button"
|
|
1583
|
+
[attr.aria-label]="strings().ViewModeToolTip"
|
|
1584
|
+
[attr.aria-expanded]="openMenu() === 'viewMode'"
|
|
1585
|
+
(click)="toggleMenu('viewMode')"
|
|
1586
|
+
>
|
|
1587
|
+
<!-- Stimulsoft's button keeps the single-page icon whatever the mode. -->
|
|
1588
|
+
<span class="nxe-toolbar-icon">
|
|
1589
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
1590
|
+
<path d="M2.5 1.5h11v13h-11z" fill="#fff" stroke="#383838" />
|
|
1591
|
+
</svg>
|
|
1592
|
+
</span>
|
|
1593
|
+
@if (showCaptions()) {
|
|
1594
|
+
<span class="nxe-toolbar-caption">{{ strings()[viewMode()] }}</span>
|
|
1595
|
+
}
|
|
1596
|
+
<span class="nxe-toolbar-arrow">
|
|
1597
|
+
<svg viewBox="0 0 8 8" aria-hidden="true">
|
|
1598
|
+
<path d="M1.5 2.5 4 5l2.5-2.5" fill="none" stroke="#787878" />
|
|
1599
|
+
</svg>
|
|
1600
|
+
</span>
|
|
1601
|
+
</button>
|
|
1602
|
+
@if (openMenu() === 'viewMode') {
|
|
1603
|
+
<!-- Stimulsoft marks no item here as the current one, unlike the zoom menu. -->
|
|
1604
|
+
<div class="nxe-toolbar-popup" role="menu">
|
|
1605
|
+
@for (mode of viewModes; track mode) {
|
|
1606
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="chooseViewMode(mode)">
|
|
1607
|
+
<span class="nxe-toolbar-item-icon">
|
|
1608
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#fff" stroke="#383838">
|
|
1609
|
+
@switch (mode) {
|
|
1610
|
+
@case ('SinglePage') {
|
|
1611
|
+
<path d="M2.5 1.5h11v13h-11z" />
|
|
1612
|
+
}
|
|
1613
|
+
@case ('Continuous') {
|
|
1614
|
+
<path d="M2.5 1.5h11v4h-11zm0 6h11v7h-11z" />
|
|
1615
|
+
}
|
|
1616
|
+
@case ('MultiplePages') {
|
|
1617
|
+
<path d="M2.5 2.5h5v5h-5zm6 0h5v5h-5zm-6 6h5v5h-5zm6 0h5v5h-5z" />
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
</svg>
|
|
1621
|
+
</span>
|
|
1622
|
+
<span class="nxe-toolbar-item-text">{{ strings()[mode] }}</span>
|
|
1623
|
+
</button>
|
|
1624
|
+
}
|
|
1625
|
+
</div>
|
|
1626
|
+
}
|
|
1627
|
+
</div>
|
|
1628
|
+
}
|
|
1629
|
+
</div>
|
|
1630
|
+
`, isInline: true, styles: [":host{display:block;box-sizing:border-box;padding-top:2px;position:relative;-webkit-user-select:none;user-select:none;font-family:Arial;font-size:12px;color:#202020}:host(.nxe-busy):after{content:\"\";position:absolute;inset:0;cursor:default}.nxe-toolbar-bar{display:flex;align-items:center;box-sizing:border-box;height:31px;padding:1px;border:1px solid #c6c6c6;border-radius:3px;background:#fff;cursor:default}.nxe-toolbar-button{display:inline-flex;align-items:center;box-sizing:border-box;height:25px;margin:1px;padding:0;border:1px solid transparent;border-radius:3px;background:transparent;color:inherit;font:inherit;white-space:nowrap;cursor:default}.nxe-toolbar-button:hover:not(:disabled),.nxe-toolbar-button[aria-expanded=true]{background:#dadada}.nxe-toolbar-button.nxe-toolbar-selected{background:#e5e5e5}.nxe-toolbar-button:disabled{color:#d6d3d6}.nxe-toolbar-button:disabled svg{opacity:.5}.nxe-toolbar-icon{display:flex;flex:none;box-sizing:border-box;width:22px;padding-left:3px}.nxe-toolbar-icon svg{width:16px;height:16px}.nxe-toolbar-arrow{display:flex;flex:none;justify-content:center;width:18px}.nxe-toolbar-arrow svg{width:8px;height:8px}.nxe-toolbar-separator{flex:none;width:1px;height:21px;background:#d4d4d4}.nxe-page-control{display:inline-flex;align-items:center;height:23px;margin:0 1px;white-space:nowrap}.nxe-page-label,.nxe-page-box-cell{padding:0 2px}.nxe-page-box-cell{display:flex}.nxe-page-box{box-sizing:border-box;width:51px;height:23px;margin:0;padding:0 0 0 4px;border:1px solid #ababab;border-radius:3px;background:#fff;color:inherit;font:inherit;line-height:21px;text-align:left;outline:none}.nxe-page-box:hover:not(:disabled){border-color:#6b6b6b}.nxe-page-box:disabled{border-color:#e1e1e1;background:#f7f7f7;color:inherit;-webkit-text-fill-color:currentColor;opacity:1}.nxe-page-box-off{opacity:.5}.nxe-toolbar-menu{position:relative;display:flex;align-items:center}.nxe-toolbar-popup{position:absolute;top:100%;left:0;z-index:10;display:flex;flex-direction:column;gap:1px;box-sizing:border-box;padding:1px;border:1px solid #ababab;border-radius:3px;background:#fff;box-shadow:0 0 7px #0000004d}.nxe-toolbar-item{display:flex;align-items:center;flex:none;box-sizing:border-box;height:24px;margin:0 1px;padding:0;border:0;border-radius:3px;background:transparent;color:#202020;font:inherit;text-align:left;white-space:nowrap;cursor:default}.nxe-toolbar-item:first-child{margin-top:1px}.nxe-toolbar-item:last-child{margin-bottom:1px}.nxe-toolbar-item-on{background:#e5e5e5}.nxe-toolbar-item:hover{background:#dadada}.nxe-toolbar-item-icon{display:flex;flex:none;box-sizing:border-box;width:22px;padding-left:3px}.nxe-toolbar-item-icon svg{width:16px;height:16px}.nxe-toolbar-item-text{padding:0 20px 0 7px}.nxe-toolbar-item-separator{flex:none;height:1px;margin:0 2px 0 30px;background:#d4d4d4}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1216
1631
|
}
|
|
1217
1632
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeViewerToolbarComponent, decorators: [{
|
|
1218
1633
|
type: Component,
|
|
1219
1634
|
args: [{ selector: 'nxe-viewer-toolbar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1220
1635
|
class: 'nxe-viewer-toolbar',
|
|
1221
1636
|
role: 'toolbar',
|
|
1222
|
-
'[style.background-color]': 'backgroundColor()',
|
|
1223
|
-
'[style.border]': 'border()',
|
|
1224
1637
|
'[style.color]': 'fontColor()',
|
|
1225
1638
|
'[style.font-family]': 'fontFamily()',
|
|
1226
|
-
'[style.justify-content]': 'justifyContent()',
|
|
1227
1639
|
'[class.nxe-busy]': 'busy()',
|
|
1228
|
-
}, template: `
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
[
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
<
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
<button
|
|
1389
|
-
type="button"
|
|
1390
|
-
class="nxe-toolbar-button
|
|
1391
|
-
[
|
|
1392
|
-
[attr.aria-
|
|
1393
|
-
(click)="
|
|
1394
|
-
>
|
|
1395
|
-
<
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1640
|
+
}, template: `
|
|
1641
|
+
<div
|
|
1642
|
+
class="nxe-toolbar-bar"
|
|
1643
|
+
[style.background-color]="backgroundColor()"
|
|
1644
|
+
[style.border]="border()"
|
|
1645
|
+
[style.justify-content]="justifyContent()"
|
|
1646
|
+
>
|
|
1647
|
+
@if (showSave()) {
|
|
1648
|
+
<div class="nxe-toolbar-menu">
|
|
1649
|
+
<button
|
|
1650
|
+
type="button"
|
|
1651
|
+
class="nxe-toolbar-button"
|
|
1652
|
+
[attr.aria-label]="strings().SaveToolTip"
|
|
1653
|
+
[attr.aria-expanded]="openMenu() === 'save'"
|
|
1654
|
+
(click)="toggleMenu('save')"
|
|
1655
|
+
>
|
|
1656
|
+
<span class="nxe-toolbar-icon">
|
|
1657
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
1658
|
+
<path d="M1.5 1.5h13v13h-13z" fill="#d890d8" stroke="#903098" />
|
|
1659
|
+
<path d="M4.5 1.5h7v4.5h-7z" fill="#fff" stroke="#903098" />
|
|
1660
|
+
<path d="M3.5 9.5h9v5h-9z" fill="#fff" stroke="#903098" />
|
|
1661
|
+
</svg>
|
|
1662
|
+
</span>
|
|
1663
|
+
@if (showCaptions()) {
|
|
1664
|
+
<span class="nxe-toolbar-caption">{{ strings().Save }}</span>
|
|
1665
|
+
}
|
|
1666
|
+
<span class="nxe-toolbar-arrow">
|
|
1667
|
+
<svg viewBox="0 0 8 8" aria-hidden="true">
|
|
1668
|
+
<path d="M1.5 2.5 4 5l2.5-2.5" fill="none" stroke="#787878" />
|
|
1669
|
+
</svg>
|
|
1670
|
+
</span>
|
|
1671
|
+
</button>
|
|
1672
|
+
@if (openMenu() === 'save') {
|
|
1673
|
+
<div class="nxe-toolbar-popup" role="menu">
|
|
1674
|
+
@for (format of saveFormats(); track format) {
|
|
1675
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="save(format)">
|
|
1676
|
+
<span class="nxe-toolbar-item-icon">
|
|
1677
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
1678
|
+
<path d="M3.5.5h7l3 3v12h-10z" fill="#fff" stroke="#6e6e6e" />
|
|
1679
|
+
<path d="M10.5.5v3h3" fill="none" stroke="#6e6e6e" />
|
|
1680
|
+
@if (format === 'Pdf') {
|
|
1681
|
+
<path d="M.5 6.5h11v5H.5z" fill="#f6d0d0" stroke="#d83b3b" />
|
|
1682
|
+
<text x="6" y="10.4" fill="#d83b3b" font-family="Arial" font-size="4.2" font-weight="bold" text-anchor="middle">PDF</text>
|
|
1683
|
+
} @else {
|
|
1684
|
+
<path d="M.5 6.5h13v5H.5z" fill="#d6e9f8" stroke="#2088d0" />
|
|
1685
|
+
<text x="7" y="10.4" fill="#2088d0" font-family="Arial" font-size="4" font-weight="bold" text-anchor="middle">HTML</text>
|
|
1686
|
+
}
|
|
1687
|
+
</svg>
|
|
1688
|
+
</span>
|
|
1689
|
+
<span class="nxe-toolbar-item-text">{{ formatCaption(format) }}</span>
|
|
1690
|
+
</button>
|
|
1691
|
+
}
|
|
1692
|
+
</div>
|
|
1693
|
+
}
|
|
1694
|
+
</div>
|
|
1695
|
+
}
|
|
1696
|
+
@if (showPanelsSeparator()) {
|
|
1697
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1698
|
+
}
|
|
1699
|
+
@if (showResources()) {
|
|
1700
|
+
<!-- A report's resources are not served yet, so there are none to show: Stimulsoft disables the button then. -->
|
|
1701
|
+
<button type="button" class="nxe-toolbar-button" disabled [attr.aria-label]="strings().Resources">
|
|
1702
|
+
<span class="nxe-toolbar-icon">
|
|
1703
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="#313131">
|
|
1704
|
+
<path d="M6.5 1.5h5l3 3v8h-8z" fill="#fff" />
|
|
1705
|
+
<path d="M11.5 1.5v3h3" />
|
|
1706
|
+
<path d="M1.5 4.5h5v10h-5z" fill="#fff" />
|
|
1707
|
+
<path d="M2.5 7.5h3m-3 2h3m-3 2h3" stroke="#717171" />
|
|
1708
|
+
</svg>
|
|
1709
|
+
</span>
|
|
1710
|
+
</button>
|
|
1711
|
+
}
|
|
1712
|
+
@if (showFindSeparator()) {
|
|
1713
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1714
|
+
}
|
|
1715
|
+
@if (showFind()) {
|
|
1716
|
+
<button
|
|
1717
|
+
type="button"
|
|
1718
|
+
class="nxe-toolbar-button"
|
|
1719
|
+
[class.nxe-toolbar-selected]="findOpen()"
|
|
1720
|
+
[attr.aria-label]="strings().FindToolTip"
|
|
1721
|
+
[attr.aria-pressed]="findOpen()"
|
|
1722
|
+
(click)="findToggle.emit()"
|
|
1723
|
+
>
|
|
1724
|
+
<span class="nxe-toolbar-icon">
|
|
1725
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none" stroke="#383838">
|
|
1726
|
+
<circle cx="9.5" cy="6.5" r="4.4" stroke-width="1.4" />
|
|
1727
|
+
<path d="M6.4 9.6 2 14" stroke-width="2" stroke-linecap="round" />
|
|
1728
|
+
</svg>
|
|
1729
|
+
</span>
|
|
1730
|
+
</button>
|
|
1731
|
+
}
|
|
1732
|
+
@if (showNavigationSeparator()) {
|
|
1733
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1734
|
+
}
|
|
1735
|
+
@if (showFirstPage()) {
|
|
1736
|
+
<button
|
|
1737
|
+
type="button"
|
|
1738
|
+
class="nxe-toolbar-button"
|
|
1739
|
+
[disabled]="atStart()"
|
|
1740
|
+
[attr.aria-label]="strings().FirstPage"
|
|
1741
|
+
(click)="goTo(0)"
|
|
1742
|
+
>
|
|
1743
|
+
<span class="nxe-toolbar-icon">
|
|
1744
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1745
|
+
<path d="M3.5 2.5h2v11h-2z" />
|
|
1746
|
+
<path d="M12.5 2.5v11l-6-5.5z" />
|
|
1747
|
+
</svg>
|
|
1748
|
+
</span>
|
|
1749
|
+
</button>
|
|
1750
|
+
}
|
|
1751
|
+
@if (showPreviousPage()) {
|
|
1752
|
+
<button
|
|
1753
|
+
type="button"
|
|
1754
|
+
class="nxe-toolbar-button"
|
|
1755
|
+
[disabled]="atStart()"
|
|
1756
|
+
[attr.aria-label]="strings().PrevPage"
|
|
1757
|
+
(click)="goTo(pageIndex() - 1)"
|
|
1758
|
+
>
|
|
1759
|
+
<span class="nxe-toolbar-icon">
|
|
1760
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1761
|
+
<path d="M10.5 2.5v11l-6-5.5z" />
|
|
1762
|
+
</svg>
|
|
1763
|
+
</span>
|
|
1764
|
+
</button>
|
|
1765
|
+
}
|
|
1766
|
+
@if (showCurrentPage()) {
|
|
1767
|
+
<span class="nxe-page-control">
|
|
1768
|
+
<span class="nxe-page-label">{{ strings().Page }}</span>
|
|
1769
|
+
<span class="nxe-page-box-cell" [class.nxe-page-box-off]="pageControlDisabled()">
|
|
1770
|
+
<input
|
|
1771
|
+
type="text"
|
|
1772
|
+
inputmode="numeric"
|
|
1773
|
+
class="nxe-page-box"
|
|
1774
|
+
[disabled]="pageControlDisabled()"
|
|
1775
|
+
[value]="pageIndex() + 1"
|
|
1776
|
+
[attr.aria-label]="strings().Page"
|
|
1777
|
+
(keydown.enter)="enterPage($event)"
|
|
1778
|
+
/>
|
|
1779
|
+
</span>
|
|
1780
|
+
<span class="nxe-page-label">{{ strings().PageOf }}</span>
|
|
1781
|
+
<span class="nxe-page-label">{{ pageCount() }}</span>
|
|
1782
|
+
</span>
|
|
1783
|
+
}
|
|
1784
|
+
@if (showNextPage()) {
|
|
1785
|
+
<button
|
|
1786
|
+
type="button"
|
|
1787
|
+
class="nxe-toolbar-button"
|
|
1788
|
+
[disabled]="atEnd()"
|
|
1789
|
+
[attr.aria-label]="strings().NextPage"
|
|
1790
|
+
(click)="goTo(pageIndex() + 1)"
|
|
1791
|
+
>
|
|
1792
|
+
<span class="nxe-toolbar-icon">
|
|
1793
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1794
|
+
<path d="M5.5 2.5v11l6-5.5z" />
|
|
1795
|
+
</svg>
|
|
1796
|
+
</span>
|
|
1797
|
+
</button>
|
|
1798
|
+
}
|
|
1799
|
+
@if (showLastPage()) {
|
|
1800
|
+
<button
|
|
1801
|
+
type="button"
|
|
1802
|
+
class="nxe-toolbar-button"
|
|
1803
|
+
[disabled]="atEnd()"
|
|
1804
|
+
[attr.aria-label]="strings().LastPage"
|
|
1805
|
+
(click)="goTo(pageCount() - 1)"
|
|
1806
|
+
>
|
|
1807
|
+
<span class="nxe-toolbar-icon">
|
|
1808
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#81c1f1" stroke="#0161b1" stroke-linejoin="round">
|
|
1809
|
+
<path d="M3.5 2.5v11l6-5.5z" />
|
|
1810
|
+
<path d="M10.5 2.5h2v11h-2z" />
|
|
1811
|
+
</svg>
|
|
1812
|
+
</span>
|
|
1813
|
+
</button>
|
|
1814
|
+
}
|
|
1815
|
+
@if (showZoom() || showViewMode()) {
|
|
1816
|
+
<span class="nxe-toolbar-separator"></span>
|
|
1817
|
+
}
|
|
1818
|
+
@if (showZoom()) {
|
|
1819
|
+
<div class="nxe-toolbar-menu">
|
|
1820
|
+
<button
|
|
1821
|
+
type="button"
|
|
1822
|
+
class="nxe-toolbar-button"
|
|
1823
|
+
[attr.aria-label]="strings().ZoomToolTip"
|
|
1824
|
+
[attr.aria-expanded]="openMenu() === 'zoom'"
|
|
1825
|
+
(click)="toggleMenu('zoom')"
|
|
1826
|
+
>
|
|
1827
|
+
<span class="nxe-toolbar-icon">
|
|
1828
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
1829
|
+
<path d="M2.5 1.5h6l3 3v5M8.5 1.5v3h3M2.5 1.5v13h7" stroke="#383838" />
|
|
1830
|
+
<circle cx="11.5" cy="11.5" r="2" stroke="#2088d0" />
|
|
1831
|
+
<path d="m13 13 1.8 1.8" stroke="#2088d0" />
|
|
1832
|
+
</svg>
|
|
1833
|
+
</span>
|
|
1834
|
+
@if (showCaptions()) {
|
|
1835
|
+
<span class="nxe-toolbar-caption">{{ zoomCaption() }}</span>
|
|
1836
|
+
}
|
|
1837
|
+
<span class="nxe-toolbar-arrow">
|
|
1838
|
+
<svg viewBox="0 0 8 8" aria-hidden="true">
|
|
1839
|
+
<path d="M1.5 2.5 4 5l2.5-2.5" fill="none" stroke="#787878" />
|
|
1840
|
+
</svg>
|
|
1841
|
+
</span>
|
|
1842
|
+
</button>
|
|
1843
|
+
@if (openMenu() === 'zoom') {
|
|
1844
|
+
<div class="nxe-toolbar-popup" role="menu">
|
|
1845
|
+
@for (percent of zoomPresets; track percent) {
|
|
1846
|
+
<button
|
|
1847
|
+
type="button"
|
|
1848
|
+
class="nxe-toolbar-item"
|
|
1849
|
+
role="menuitem"
|
|
1850
|
+
[class.nxe-toolbar-item-on]="isZoom(percent)"
|
|
1851
|
+
(click)="chooseZoom(percent)"
|
|
1852
|
+
>
|
|
1853
|
+
<span class="nxe-toolbar-item-icon"></span>
|
|
1854
|
+
<span class="nxe-toolbar-item-text">{{ percent }}%</span>
|
|
1855
|
+
</button>
|
|
1856
|
+
}
|
|
1857
|
+
<span class="nxe-toolbar-item-separator"></span>
|
|
1858
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="fit('page')">
|
|
1859
|
+
<span class="nxe-toolbar-item-icon">
|
|
1860
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
1861
|
+
<path d="M3.5 1.5h9v13h-9z" fill="#fff" stroke="#383838" />
|
|
1862
|
+
<path d="M8 3.5v9M6 5.5l2-2 2 2M6 10.5l2 2 2-2" stroke="#2088d0" />
|
|
1863
|
+
</svg>
|
|
1864
|
+
</span>
|
|
1865
|
+
<span class="nxe-toolbar-item-text">{{ strings().ZoomOnePage }}</span>
|
|
1866
|
+
</button>
|
|
1867
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="fit('width')">
|
|
1868
|
+
<span class="nxe-toolbar-item-icon">
|
|
1869
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="none">
|
|
1870
|
+
<path d="M1.5 3.5h13v9h-13z" fill="#fff" stroke="#383838" />
|
|
1871
|
+
<path d="M3.5 8h9M5.5 6l-2 2 2 2M10.5 6l2 2-2 2" stroke="#2088d0" />
|
|
1872
|
+
</svg>
|
|
1873
|
+
</span>
|
|
1874
|
+
<span class="nxe-toolbar-item-text">{{ strings().ZoomPageWidth }}</span>
|
|
1875
|
+
</button>
|
|
1876
|
+
</div>
|
|
1877
|
+
}
|
|
1878
|
+
</div>
|
|
1879
|
+
}
|
|
1880
|
+
@if (showViewMode()) {
|
|
1881
|
+
<div class="nxe-toolbar-menu">
|
|
1882
|
+
<button
|
|
1883
|
+
type="button"
|
|
1884
|
+
class="nxe-toolbar-button"
|
|
1885
|
+
[attr.aria-label]="strings().ViewModeToolTip"
|
|
1886
|
+
[attr.aria-expanded]="openMenu() === 'viewMode'"
|
|
1887
|
+
(click)="toggleMenu('viewMode')"
|
|
1888
|
+
>
|
|
1889
|
+
<!-- Stimulsoft's button keeps the single-page icon whatever the mode. -->
|
|
1890
|
+
<span class="nxe-toolbar-icon">
|
|
1891
|
+
<svg viewBox="0 0 16 16" aria-hidden="true">
|
|
1892
|
+
<path d="M2.5 1.5h11v13h-11z" fill="#fff" stroke="#383838" />
|
|
1893
|
+
</svg>
|
|
1894
|
+
</span>
|
|
1895
|
+
@if (showCaptions()) {
|
|
1896
|
+
<span class="nxe-toolbar-caption">{{ strings()[viewMode()] }}</span>
|
|
1897
|
+
}
|
|
1898
|
+
<span class="nxe-toolbar-arrow">
|
|
1899
|
+
<svg viewBox="0 0 8 8" aria-hidden="true">
|
|
1900
|
+
<path d="M1.5 2.5 4 5l2.5-2.5" fill="none" stroke="#787878" />
|
|
1901
|
+
</svg>
|
|
1902
|
+
</span>
|
|
1903
|
+
</button>
|
|
1904
|
+
@if (openMenu() === 'viewMode') {
|
|
1905
|
+
<!-- Stimulsoft marks no item here as the current one, unlike the zoom menu. -->
|
|
1906
|
+
<div class="nxe-toolbar-popup" role="menu">
|
|
1907
|
+
@for (mode of viewModes; track mode) {
|
|
1908
|
+
<button type="button" class="nxe-toolbar-item" role="menuitem" (click)="chooseViewMode(mode)">
|
|
1909
|
+
<span class="nxe-toolbar-item-icon">
|
|
1910
|
+
<svg viewBox="0 0 16 16" aria-hidden="true" fill="#fff" stroke="#383838">
|
|
1911
|
+
@switch (mode) {
|
|
1912
|
+
@case ('SinglePage') {
|
|
1913
|
+
<path d="M2.5 1.5h11v13h-11z" />
|
|
1914
|
+
}
|
|
1915
|
+
@case ('Continuous') {
|
|
1916
|
+
<path d="M2.5 1.5h11v4h-11zm0 6h11v7h-11z" />
|
|
1917
|
+
}
|
|
1918
|
+
@case ('MultiplePages') {
|
|
1919
|
+
<path d="M2.5 2.5h5v5h-5zm6 0h5v5h-5zm-6 6h5v5h-5zm6 0h5v5h-5z" />
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
</svg>
|
|
1923
|
+
</span>
|
|
1924
|
+
<span class="nxe-toolbar-item-text">{{ strings()[mode] }}</span>
|
|
1925
|
+
</button>
|
|
1926
|
+
}
|
|
1927
|
+
</div>
|
|
1928
|
+
}
|
|
1929
|
+
</div>
|
|
1930
|
+
}
|
|
1931
|
+
</div>
|
|
1932
|
+
`, styles: [":host{display:block;box-sizing:border-box;padding-top:2px;position:relative;-webkit-user-select:none;user-select:none;font-family:Arial;font-size:12px;color:#202020}:host(.nxe-busy):after{content:\"\";position:absolute;inset:0;cursor:default}.nxe-toolbar-bar{display:flex;align-items:center;box-sizing:border-box;height:31px;padding:1px;border:1px solid #c6c6c6;border-radius:3px;background:#fff;cursor:default}.nxe-toolbar-button{display:inline-flex;align-items:center;box-sizing:border-box;height:25px;margin:1px;padding:0;border:1px solid transparent;border-radius:3px;background:transparent;color:inherit;font:inherit;white-space:nowrap;cursor:default}.nxe-toolbar-button:hover:not(:disabled),.nxe-toolbar-button[aria-expanded=true]{background:#dadada}.nxe-toolbar-button.nxe-toolbar-selected{background:#e5e5e5}.nxe-toolbar-button:disabled{color:#d6d3d6}.nxe-toolbar-button:disabled svg{opacity:.5}.nxe-toolbar-icon{display:flex;flex:none;box-sizing:border-box;width:22px;padding-left:3px}.nxe-toolbar-icon svg{width:16px;height:16px}.nxe-toolbar-arrow{display:flex;flex:none;justify-content:center;width:18px}.nxe-toolbar-arrow svg{width:8px;height:8px}.nxe-toolbar-separator{flex:none;width:1px;height:21px;background:#d4d4d4}.nxe-page-control{display:inline-flex;align-items:center;height:23px;margin:0 1px;white-space:nowrap}.nxe-page-label,.nxe-page-box-cell{padding:0 2px}.nxe-page-box-cell{display:flex}.nxe-page-box{box-sizing:border-box;width:51px;height:23px;margin:0;padding:0 0 0 4px;border:1px solid #ababab;border-radius:3px;background:#fff;color:inherit;font:inherit;line-height:21px;text-align:left;outline:none}.nxe-page-box:hover:not(:disabled){border-color:#6b6b6b}.nxe-page-box:disabled{border-color:#e1e1e1;background:#f7f7f7;color:inherit;-webkit-text-fill-color:currentColor;opacity:1}.nxe-page-box-off{opacity:.5}.nxe-toolbar-menu{position:relative;display:flex;align-items:center}.nxe-toolbar-popup{position:absolute;top:100%;left:0;z-index:10;display:flex;flex-direction:column;gap:1px;box-sizing:border-box;padding:1px;border:1px solid #ababab;border-radius:3px;background:#fff;box-shadow:0 0 7px #0000004d}.nxe-toolbar-item{display:flex;align-items:center;flex:none;box-sizing:border-box;height:24px;margin:0 1px;padding:0;border:0;border-radius:3px;background:transparent;color:#202020;font:inherit;text-align:left;white-space:nowrap;cursor:default}.nxe-toolbar-item:first-child{margin-top:1px}.nxe-toolbar-item:last-child{margin-bottom:1px}.nxe-toolbar-item-on{background:#e5e5e5}.nxe-toolbar-item:hover{background:#dadada}.nxe-toolbar-item-icon{display:flex;flex:none;box-sizing:border-box;width:22px;padding-left:3px}.nxe-toolbar-item-icon svg{width:16px;height:16px}.nxe-toolbar-item-text{padding:0 20px 0 7px}.nxe-toolbar-item-separator{flex:none;height:1px;margin:0 2px 0 30px;background:#d4d4d4}\n"] }]
|
|
1933
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], pageIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageIndex", required: false }] }], pageCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageCount", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], strings: [{ type: i0.Input, args: [{ isSignal: true, alias: "strings", required: false }] }], viewMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "viewMode", required: false }] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }], findOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "findOpen", required: false }] }], goToPage: [{ type: i0.Output, args: ["goToPage"] }], viewModeChange: [{ type: i0.Output, args: ["viewModeChange"] }], zoomChange: [{ type: i0.Output, args: ["zoomChange"] }], fitRequest: [{ type: i0.Output, args: ["fitRequest"] }], saveRequest: [{ type: i0.Output, args: ["saveRequest"] }], findToggle: [{ type: i0.Output, args: ["findToggle"] }] } });
|
|
1437
1934
|
|
|
1438
1935
|
/** Stimulsoft's scroll tracker waits this long after scrolling stops before it moves the page. */
|
|
1439
1936
|
const SCROLL_SETTLE_MS = 300;
|
|
@@ -1443,6 +1940,8 @@ const SCROLL_STEP_MS = 30;
|
|
|
1443
1940
|
/** The viewer never measures itself smaller than this, nor closer than this to the window's bottom. */
|
|
1444
1941
|
const MIN_PANEL_HEIGHT = 200;
|
|
1445
1942
|
const VIEWER_BOTTOM_GAP = 16;
|
|
1943
|
+
/** Stimulsoft scrolls a hit to its own height plus this far below the top of the report panel. */
|
|
1944
|
+
const FIND_SCROLL_GAP = 50;
|
|
1446
1945
|
/**
|
|
1447
1946
|
* A report served by `Nextera.Reports.Angular.NetCore`, shown page by page.
|
|
1448
1947
|
*
|
|
@@ -1520,6 +2019,25 @@ class NxeReportViewerComponent {
|
|
|
1520
2019
|
scrollSettled = null;
|
|
1521
2020
|
toolbarVisible = computed(() => this.session.options()?.toolbar.visible !== false, /* @ts-ignore */
|
|
1522
2021
|
...(ngDevMode ? [{ debugName: "toolbarVisible" }] : /* istanbul ignore next */ []));
|
|
2022
|
+
/** See `.nxe-viewer-bookmarks`: the whole of Stimulsoft's bookmarks panel at width 0. */
|
|
2023
|
+
showBookmarksStrip = computed(() => this.session.report() !== null && this.appearance()?.bookmarksTreeWidth === 0, /* @ts-ignore */
|
|
2024
|
+
...(ngDevMode ? [{ debugName: "showBookmarksStrip" }] : /* istanbul ignore next */ []));
|
|
2025
|
+
/** Whether the find panel is open; Stimulsoft's Find button opens and closes it. */
|
|
2026
|
+
findOpen = signal(false, /* @ts-ignore */
|
|
2027
|
+
...(ngDevMode ? [{ debugName: "findOpen" }] : /* istanbul ignore next */ []));
|
|
2028
|
+
/** The search on show: what was looked for, the elements found, and which one is current. */
|
|
2029
|
+
findKey = null;
|
|
2030
|
+
hits = [];
|
|
2031
|
+
currentHit = 0;
|
|
2032
|
+
lastFind = null;
|
|
2033
|
+
/**
|
|
2034
|
+
* The pages whose HTML is on screen, so a search can follow them: in SinglePage the page changes
|
|
2035
|
+
* under it, and in the scrolling modes pages keep arriving as they are fetched.
|
|
2036
|
+
*/
|
|
2037
|
+
renderedPages = computed(() => this.shownPages()
|
|
2038
|
+
.map((page) => `${page.index}:${page.html === null ? 0 : 1}`)
|
|
2039
|
+
.join(','), /* @ts-ignore */
|
|
2040
|
+
...(ngDevMode ? [{ debugName: "renderedPages" }] : /* istanbul ignore next */ []));
|
|
1523
2041
|
pageCss = computed(() => this.session.report()?.pageCss ?? '', /* @ts-ignore */
|
|
1524
2042
|
...(ngDevMode ? [{ debugName: "pageCss" }] : /* istanbul ignore next */ []));
|
|
1525
2043
|
showPageShadow = computed(() => this.appearance()?.showPageShadow === true, /* @ts-ignore */
|
|
@@ -1580,6 +2098,117 @@ class NxeReportViewerComponent {
|
|
|
1580
2098
|
this.session.failed
|
|
1581
2099
|
.pipe(takeUntilDestroyed())
|
|
1582
2100
|
.subscribe((failure) => this.error.emit(failure));
|
|
2101
|
+
// While a search is on show, new page HTML on screen means the search runs again over it.
|
|
2102
|
+
toObservable(this.renderedPages)
|
|
2103
|
+
.pipe(distinctUntilChanged(), skip(1), takeUntilDestroyed())
|
|
2104
|
+
.subscribe(() => {
|
|
2105
|
+
if (this.findOpen() && this.lastFind !== null) {
|
|
2106
|
+
requestAnimationFrame(() => this.refreshFind());
|
|
2107
|
+
}
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
/** The Find button opens the panel, or closes it and takes the search's highlights away. */
|
|
2111
|
+
toggleFind() {
|
|
2112
|
+
if (this.findOpen()) {
|
|
2113
|
+
this.closeFind();
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
this.findOpen.set(true);
|
|
2117
|
+
this.afterPagesSettle();
|
|
2118
|
+
}
|
|
2119
|
+
closeFind() {
|
|
2120
|
+
this.findOpen.set(false);
|
|
2121
|
+
this.resetFind();
|
|
2122
|
+
this.afterPagesSettle();
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* A search from the find panel, run as Stimulsoft runs it (angular-viewer-panels-chrome.md §1.3,
|
|
2126
|
+
* §1.6): empty text clears the highlights; new text, or a changed option, finds afresh and makes the
|
|
2127
|
+
* first hit current; the same search again steps to the next or previous hit, round the ends. Each
|
|
2128
|
+
* current hit is scrolled into view. Stimulsoft finds only in the pages it has on screen; this viewer
|
|
2129
|
+
* fetches the pages a scrolling mode has not fetched yet, and finds in them as they arrive.
|
|
2130
|
+
*/
|
|
2131
|
+
find(request) {
|
|
2132
|
+
this.lastFind = request;
|
|
2133
|
+
if (request.text === '') {
|
|
2134
|
+
this.resetFind();
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
const key = `${request.matchCase ? 1 : 0}${request.wholeWord ? 1 : 0}${request.text}`;
|
|
2138
|
+
if (key !== this.findKey) {
|
|
2139
|
+
this.findKey = key;
|
|
2140
|
+
this.requestAllPages();
|
|
2141
|
+
this.runFind(request, 0);
|
|
2142
|
+
this.revealHit(true);
|
|
2143
|
+
return;
|
|
2144
|
+
}
|
|
2145
|
+
if (this.hits.length === 0)
|
|
2146
|
+
return;
|
|
2147
|
+
const previous = this.currentHit;
|
|
2148
|
+
const count = this.hits.length;
|
|
2149
|
+
this.currentHit = request.forward ? (previous + 1) % count : (previous - 1 + count) % count;
|
|
2150
|
+
markCurrent(this.hits, previous, this.currentHit);
|
|
2151
|
+
this.revealHit(false);
|
|
2152
|
+
}
|
|
2153
|
+
/** Finds again over the pages now on screen, keeping the current hit when it is still there. */
|
|
2154
|
+
refreshFind() {
|
|
2155
|
+
const request = this.lastFind;
|
|
2156
|
+
if (request === null || request.text === '')
|
|
2157
|
+
return;
|
|
2158
|
+
const kept = this.hits[this.currentHit];
|
|
2159
|
+
this.runFind(request, 0);
|
|
2160
|
+
const index = kept ? this.hits.indexOf(kept) : -1;
|
|
2161
|
+
if (index > 0) {
|
|
2162
|
+
markCurrent(this.hits, 0, index);
|
|
2163
|
+
this.currentHit = index;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
runFind(request, current) {
|
|
2167
|
+
const roots = this.pageRoots();
|
|
2168
|
+
clearHits(roots);
|
|
2169
|
+
this.hits = findHits(roots, request.text, request);
|
|
2170
|
+
this.currentHit = current;
|
|
2171
|
+
labelHits(this.hits, this.currentHit);
|
|
2172
|
+
}
|
|
2173
|
+
resetFind() {
|
|
2174
|
+
clearHits(this.pageRoots());
|
|
2175
|
+
this.hits = [];
|
|
2176
|
+
this.currentHit = 0;
|
|
2177
|
+
this.findKey = null;
|
|
2178
|
+
this.lastFind = null;
|
|
2179
|
+
}
|
|
2180
|
+
/** Each page on screen keeps its HTML in its own shadow root. */
|
|
2181
|
+
pageRoots() {
|
|
2182
|
+
const box = this.pagesBox()?.nativeElement;
|
|
2183
|
+
if (!box)
|
|
2184
|
+
return [];
|
|
2185
|
+
return Array.from(box.querySelectorAll('nxe-report-page'))
|
|
2186
|
+
.map((page) => page.shadowRoot)
|
|
2187
|
+
.filter((root) => root !== null);
|
|
2188
|
+
}
|
|
2189
|
+
requestAllPages() {
|
|
2190
|
+
if (this.viewMode() === 'SinglePage')
|
|
2191
|
+
return;
|
|
2192
|
+
for (let index = 0; index < this.session.pageCount(); index++)
|
|
2193
|
+
this.session.requestPage(index);
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Scrolls the current hit to its own height plus 50 px below the top of the report panel, over
|
|
2197
|
+
* 350 ms: always after a fresh search, and after a step only when the hit is out of view. That is
|
|
2198
|
+
* what Stimulsoft's viewer was seen to do; the exact place it scrolls to also carries offsets of its
|
|
2199
|
+
* own page markup, which this viewer's pages do not have.
|
|
2200
|
+
*/
|
|
2201
|
+
revealHit(always) {
|
|
2202
|
+
const box = this.pagesBox()?.nativeElement;
|
|
2203
|
+
const hit = this.hits[this.currentHit];
|
|
2204
|
+
if (!box || !hit)
|
|
2205
|
+
return;
|
|
2206
|
+
const panel = box.getBoundingClientRect();
|
|
2207
|
+
const target = hit.getBoundingClientRect();
|
|
2208
|
+
if (!always && target.top >= panel.top && target.bottom <= panel.bottom)
|
|
2209
|
+
return;
|
|
2210
|
+
const to = box.scrollTop + target.top - panel.top - (target.height + FIND_SCROLL_GAP);
|
|
2211
|
+
this.animateScroll(box, Math.max(0, to));
|
|
1583
2212
|
}
|
|
1584
2213
|
/**
|
|
1585
2214
|
* A change to any input reloads, as Stimulsoft's viewer does: its `ngOnChanges` never looks at
|
|
@@ -1662,10 +2291,11 @@ class NxeReportViewerComponent {
|
|
|
1662
2291
|
if (!root || !this.appearance()?.scrollbarsMode)
|
|
1663
2292
|
return;
|
|
1664
2293
|
const rect = root.getBoundingClientRect();
|
|
1665
|
-
|
|
1666
|
-
//
|
|
1667
|
-
if (this.measuredHeight() === null && rect.
|
|
2294
|
+
// A height that resolved is left alone, as Stimulsoft leaves it, even when it ends at the very
|
|
2295
|
+
// bottom of the window; one that grew past the window's bottom is taken over.
|
|
2296
|
+
if (this.measuredHeight() === null && rect.bottom <= window.innerHeight + 1)
|
|
1668
2297
|
return;
|
|
2298
|
+
const room = Math.max(MIN_PANEL_HEIGHT, Math.round(window.innerHeight - rect.top - VIEWER_BOTTOM_GAP));
|
|
1669
2299
|
this.measuredHeight.set(`${room}px`);
|
|
1670
2300
|
}
|
|
1671
2301
|
/** Every page whose box meets the panel, plus one either side, so scrolling stays ahead of the eye. */
|
|
@@ -1709,8 +2339,11 @@ class NxeReportViewerComponent {
|
|
|
1709
2339
|
const element = box?.querySelectorAll('nxe-report-page')[index];
|
|
1710
2340
|
if (!box || !(element instanceof HTMLElement))
|
|
1711
2341
|
return;
|
|
2342
|
+
this.animateScroll(box, element.offsetTop - box.offsetTop);
|
|
2343
|
+
}
|
|
2344
|
+
/** Scrolls the report panel over 350 ms in steps of about 30 ms, as Stimulsoft's scrolls all run. */
|
|
2345
|
+
animateScroll(box, to) {
|
|
1712
2346
|
const from = box.scrollTop;
|
|
1713
|
-
const to = element.offsetTop - box.offsetTop;
|
|
1714
2347
|
const started = performance.now();
|
|
1715
2348
|
this.scrollingToPage = true;
|
|
1716
2349
|
const step = () => {
|
|
@@ -1756,121 +2389,143 @@ class NxeReportViewerComponent {
|
|
|
1756
2389
|
};
|
|
1757
2390
|
}
|
|
1758
2391
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeReportViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1759
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeReportViewerComponent, isStandalone: true, selector: "nxe-report-viewer", inputs: { requestUrl: { classPropertyName: "requestUrl", publicName: "requestUrl", isSignal: true, isRequired: false, transformFunction: null }, action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: false, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, backgroundColor: { classPropertyName: "backgroundColor", publicName: "backgroundColor", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, postParametersFunction: { classPropertyName: "postParametersFunction", publicName: "postParametersFunction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", error: "error" }, host: { listeners: { "window:resize": "onWindowResize()" } }, providers: [NxeViewerClientService, NxeViewerSessionService], viewQueries: [{ propertyName: "pagesBox", first: true, predicate: ["pagesBox"], descendants: true, isSignal: true }, { propertyName: "rootBox", first: true, predicate: ["rootBox"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
1760
|
-
<div
|
|
1761
|
-
#rootBox
|
|
1762
|
-
class="nxe-viewer-root"
|
|
1763
|
-
[style]="style()"
|
|
1764
|
-
[style.background-color]="rootBackgroundColor()"
|
|
1765
|
-
[style.width]="rootWidth()"
|
|
1766
|
-
[style.height]="rootHeight()"
|
|
1767
|
-
>
|
|
1768
|
-
@if (toolbarVisible()) {
|
|
1769
|
-
<nxe-viewer-toolbar
|
|
1770
|
-
[options]="session.options()"
|
|
1771
|
-
[pageIndex]="session.pageIndex()"
|
|
1772
|
-
[pageCount]="session.pageCount()"
|
|
1773
|
-
[busy]="session.busy()"
|
|
1774
|
-
[viewMode]="viewMode()"
|
|
1775
|
-
[zoom]="zoom()"
|
|
1776
|
-
|
|
1777
|
-
(
|
|
1778
|
-
(
|
|
1779
|
-
(
|
|
1780
|
-
(
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
(
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
2392
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: NxeReportViewerComponent, isStandalone: true, selector: "nxe-report-viewer", inputs: { requestUrl: { classPropertyName: "requestUrl", publicName: "requestUrl", isSignal: true, isRequired: false, transformFunction: null }, action: { classPropertyName: "action", publicName: "action", isSignal: true, isRequired: false, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, backgroundColor: { classPropertyName: "backgroundColor", publicName: "backgroundColor", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, postParametersFunction: { classPropertyName: "postParametersFunction", publicName: "postParametersFunction", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { loaded: "loaded", error: "error" }, host: { listeners: { "window:resize": "onWindowResize()" } }, providers: [NxeViewerClientService, NxeViewerSessionService], viewQueries: [{ propertyName: "pagesBox", first: true, predicate: ["pagesBox"], descendants: true, isSignal: true }, { propertyName: "rootBox", first: true, predicate: ["rootBox"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
2393
|
+
<div
|
|
2394
|
+
#rootBox
|
|
2395
|
+
class="nxe-viewer-root"
|
|
2396
|
+
[style]="style()"
|
|
2397
|
+
[style.background-color]="rootBackgroundColor()"
|
|
2398
|
+
[style.width]="rootWidth()"
|
|
2399
|
+
[style.height]="rootHeight()"
|
|
2400
|
+
>
|
|
2401
|
+
@if (toolbarVisible()) {
|
|
2402
|
+
<nxe-viewer-toolbar
|
|
2403
|
+
[options]="session.options()"
|
|
2404
|
+
[pageIndex]="session.pageIndex()"
|
|
2405
|
+
[pageCount]="session.pageCount()"
|
|
2406
|
+
[busy]="session.busy()"
|
|
2407
|
+
[viewMode]="viewMode()"
|
|
2408
|
+
[zoom]="zoom()"
|
|
2409
|
+
[findOpen]="findOpen()"
|
|
2410
|
+
(goToPage)="navigateTo($event)"
|
|
2411
|
+
(viewModeChange)="setViewMode($event)"
|
|
2412
|
+
(zoomChange)="setZoom($event)"
|
|
2413
|
+
(fitRequest)="fitTo($event)"
|
|
2414
|
+
(saveRequest)="session.exportTo($event)"
|
|
2415
|
+
(findToggle)="toggleFind()"
|
|
2416
|
+
/>
|
|
2417
|
+
}
|
|
2418
|
+
@if (findOpen()) {
|
|
2419
|
+
<nxe-viewer-find-panel (search)="find($event)" (closed)="closeFind()" />
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
<div class="nxe-viewer-body">
|
|
2423
|
+
@if (showBookmarksStrip()) {
|
|
2424
|
+
<div class="nxe-viewer-bookmarks"></div>
|
|
2425
|
+
}
|
|
2426
|
+
<div
|
|
2427
|
+
#pagesBox
|
|
2428
|
+
class="nxe-viewer-pages"
|
|
2429
|
+
[class.nxe-viewer-continuous]="viewMode() === 'Continuous'"
|
|
2430
|
+
[class.nxe-viewer-pages-beside]="showBookmarksStrip()"
|
|
2431
|
+
[style.text-align]="pageAlignment()"
|
|
2432
|
+
[style.overflow]="pagesOverflow()"
|
|
2433
|
+
(scroll)="onScroll()"
|
|
2434
|
+
>
|
|
2435
|
+
@for (page of shownPages(); track page.index) {
|
|
2436
|
+
<nxe-report-page
|
|
2437
|
+
class="nxe-viewer-page"
|
|
2438
|
+
[class.nxe-viewer-page-shadow]="showPageShadow()"
|
|
2439
|
+
[style.border]="pageBorder()"
|
|
2440
|
+
[page]="page.size"
|
|
2441
|
+
[html]="page.html"
|
|
2442
|
+
[css]="pageCss()"
|
|
2443
|
+
[zoom]="zoom()"
|
|
2444
|
+
/>
|
|
2445
|
+
}
|
|
2446
|
+
</div>
|
|
2447
|
+
</div>
|
|
2448
|
+
|
|
2449
|
+
@if (session.failure(); as failure) {
|
|
2450
|
+
<div class="nxe-viewer-failure" role="alert">
|
|
2451
|
+
<span class="nxe-viewer-failure-text">{{ failure.message }}</span>
|
|
2452
|
+
<button type="button" class="nxe-viewer-failure-close" (click)="session.dismissFailure()">
|
|
2453
|
+
×
|
|
2454
|
+
</button>
|
|
2455
|
+
</div>
|
|
2456
|
+
}
|
|
2457
|
+
</div>
|
|
2458
|
+
`, isInline: true, styles: [":host{display:inline}.nxe-viewer-root{display:inline-flex;flex-direction:column;box-sizing:border-box;position:relative}.nxe-viewer-body{position:relative;display:flex;flex:1 1 auto;min-height:0;margin-top:2px}.nxe-viewer-bookmarks{position:absolute;top:0;bottom:0;left:0;box-sizing:border-box;width:5px;border-left:1px solid #c6c6c6;border-right:1px solid #c6c6c6;background:#fff}.nxe-viewer-pages{position:relative;flex:1 1 auto;min-width:0;box-sizing:border-box;padding:0 10px}.nxe-viewer-pages-beside{margin-left:2px}.nxe-viewer-page{display:inline-block;vertical-align:top;margin:10px}.nxe-viewer-continuous .nxe-viewer-page{display:block;margin:10px auto}.nxe-viewer-page-shadow{box-shadow:2px 2px 6px #0000004d}.nxe-viewer-failure{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:8px;padding:8px 12px;background:#fdecea;color:#611a15;font-size:13px}.nxe-viewer-failure-text{flex:1 1 auto}.nxe-viewer-failure-close{border:0;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer}\n"], dependencies: [{ kind: "component", type: NxeReportPageComponent, selector: "nxe-report-page", inputs: ["page", "html", "css", "zoom"] }, { kind: "component", type: NxeViewerFindPanelComponent, selector: "nxe-viewer-find-panel", inputs: ["strings"], outputs: ["search", "closed"] }, { kind: "component", type: NxeViewerToolbarComponent, selector: "nxe-viewer-toolbar", inputs: ["options", "pageIndex", "pageCount", "busy", "strings", "viewMode", "zoom", "findOpen"], outputs: ["goToPage", "viewModeChange", "zoomChange", "fitRequest", "saveRequest", "findToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1815
2459
|
}
|
|
1816
2460
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: NxeReportViewerComponent, decorators: [{
|
|
1817
2461
|
type: Component,
|
|
1818
|
-
args: [{ selector: 'nxe-report-viewer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NxeReportPageComponent, NxeViewerToolbarComponent], providers: [NxeViewerClientService, NxeViewerSessionService], host: { '(window:resize)': 'onWindowResize()' }, template: `
|
|
1819
|
-
<div
|
|
1820
|
-
#rootBox
|
|
1821
|
-
class="nxe-viewer-root"
|
|
1822
|
-
[style]="style()"
|
|
1823
|
-
[style.background-color]="rootBackgroundColor()"
|
|
1824
|
-
[style.width]="rootWidth()"
|
|
1825
|
-
[style.height]="rootHeight()"
|
|
1826
|
-
>
|
|
1827
|
-
@if (toolbarVisible()) {
|
|
1828
|
-
<nxe-viewer-toolbar
|
|
1829
|
-
[options]="session.options()"
|
|
1830
|
-
[pageIndex]="session.pageIndex()"
|
|
1831
|
-
[pageCount]="session.pageCount()"
|
|
1832
|
-
[busy]="session.busy()"
|
|
1833
|
-
[viewMode]="viewMode()"
|
|
1834
|
-
[zoom]="zoom()"
|
|
1835
|
-
|
|
1836
|
-
(
|
|
1837
|
-
(
|
|
1838
|
-
(
|
|
1839
|
-
(
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
(
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
2462
|
+
args: [{ selector: 'nxe-report-viewer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NxeReportPageComponent, NxeViewerFindPanelComponent, NxeViewerToolbarComponent], providers: [NxeViewerClientService, NxeViewerSessionService], host: { '(window:resize)': 'onWindowResize()' }, template: `
|
|
2463
|
+
<div
|
|
2464
|
+
#rootBox
|
|
2465
|
+
class="nxe-viewer-root"
|
|
2466
|
+
[style]="style()"
|
|
2467
|
+
[style.background-color]="rootBackgroundColor()"
|
|
2468
|
+
[style.width]="rootWidth()"
|
|
2469
|
+
[style.height]="rootHeight()"
|
|
2470
|
+
>
|
|
2471
|
+
@if (toolbarVisible()) {
|
|
2472
|
+
<nxe-viewer-toolbar
|
|
2473
|
+
[options]="session.options()"
|
|
2474
|
+
[pageIndex]="session.pageIndex()"
|
|
2475
|
+
[pageCount]="session.pageCount()"
|
|
2476
|
+
[busy]="session.busy()"
|
|
2477
|
+
[viewMode]="viewMode()"
|
|
2478
|
+
[zoom]="zoom()"
|
|
2479
|
+
[findOpen]="findOpen()"
|
|
2480
|
+
(goToPage)="navigateTo($event)"
|
|
2481
|
+
(viewModeChange)="setViewMode($event)"
|
|
2482
|
+
(zoomChange)="setZoom($event)"
|
|
2483
|
+
(fitRequest)="fitTo($event)"
|
|
2484
|
+
(saveRequest)="session.exportTo($event)"
|
|
2485
|
+
(findToggle)="toggleFind()"
|
|
2486
|
+
/>
|
|
2487
|
+
}
|
|
2488
|
+
@if (findOpen()) {
|
|
2489
|
+
<nxe-viewer-find-panel (search)="find($event)" (closed)="closeFind()" />
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
<div class="nxe-viewer-body">
|
|
2493
|
+
@if (showBookmarksStrip()) {
|
|
2494
|
+
<div class="nxe-viewer-bookmarks"></div>
|
|
2495
|
+
}
|
|
2496
|
+
<div
|
|
2497
|
+
#pagesBox
|
|
2498
|
+
class="nxe-viewer-pages"
|
|
2499
|
+
[class.nxe-viewer-continuous]="viewMode() === 'Continuous'"
|
|
2500
|
+
[class.nxe-viewer-pages-beside]="showBookmarksStrip()"
|
|
2501
|
+
[style.text-align]="pageAlignment()"
|
|
2502
|
+
[style.overflow]="pagesOverflow()"
|
|
2503
|
+
(scroll)="onScroll()"
|
|
2504
|
+
>
|
|
2505
|
+
@for (page of shownPages(); track page.index) {
|
|
2506
|
+
<nxe-report-page
|
|
2507
|
+
class="nxe-viewer-page"
|
|
2508
|
+
[class.nxe-viewer-page-shadow]="showPageShadow()"
|
|
2509
|
+
[style.border]="pageBorder()"
|
|
2510
|
+
[page]="page.size"
|
|
2511
|
+
[html]="page.html"
|
|
2512
|
+
[css]="pageCss()"
|
|
2513
|
+
[zoom]="zoom()"
|
|
2514
|
+
/>
|
|
2515
|
+
}
|
|
2516
|
+
</div>
|
|
2517
|
+
</div>
|
|
2518
|
+
|
|
2519
|
+
@if (session.failure(); as failure) {
|
|
2520
|
+
<div class="nxe-viewer-failure" role="alert">
|
|
2521
|
+
<span class="nxe-viewer-failure-text">{{ failure.message }}</span>
|
|
2522
|
+
<button type="button" class="nxe-viewer-failure-close" (click)="session.dismissFailure()">
|
|
2523
|
+
×
|
|
2524
|
+
</button>
|
|
2525
|
+
</div>
|
|
2526
|
+
}
|
|
2527
|
+
</div>
|
|
2528
|
+
`, styles: [":host{display:inline}.nxe-viewer-root{display:inline-flex;flex-direction:column;box-sizing:border-box;position:relative}.nxe-viewer-body{position:relative;display:flex;flex:1 1 auto;min-height:0;margin-top:2px}.nxe-viewer-bookmarks{position:absolute;top:0;bottom:0;left:0;box-sizing:border-box;width:5px;border-left:1px solid #c6c6c6;border-right:1px solid #c6c6c6;background:#fff}.nxe-viewer-pages{position:relative;flex:1 1 auto;min-width:0;box-sizing:border-box;padding:0 10px}.nxe-viewer-pages-beside{margin-left:2px}.nxe-viewer-page{display:inline-block;vertical-align:top;margin:10px}.nxe-viewer-continuous .nxe-viewer-page{display:block;margin:10px auto}.nxe-viewer-page-shadow{box-shadow:2px 2px 6px #0000004d}.nxe-viewer-failure{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:8px;padding:8px 12px;background:#fdecea;color:#611a15;font-size:13px}.nxe-viewer-failure-text{flex:1 1 auto}.nxe-viewer-failure-close{border:0;background:transparent;color:inherit;font-size:18px;line-height:1;cursor:pointer}\n"] }]
|
|
1874
2529
|
}], ctorParameters: () => [], propDecorators: { requestUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "requestUrl", required: false }] }], action: [{ type: i0.Input, args: [{ isSignal: true, alias: "action", required: false }] }], properties: [{ type: i0.Input, args: [{ isSignal: true, alias: "properties", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], backgroundColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundColor", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], postParametersFunction: [{ type: i0.Input, args: [{ isSignal: true, alias: "postParametersFunction", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], error: [{ type: i0.Output, args: ["error"] }], pagesBox: [{ type: i0.ViewChild, args: ['pagesBox', { isSignal: true }] }], rootBox: [{ type: i0.ViewChild, args: ['rootBox', { isSignal: true }] }] } });
|
|
1875
2530
|
|
|
1876
2531
|
/*
|