@broadpaper/renderer 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/LICENSE +143 -0
- package/THIRD-PARTY-NOTICES.md +255 -0
- package/dist/index.d.ts +372 -0
- package/dist/index.js +1109 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { VNode, Measurer, RenderTreeOptions, ResolvedNode, GeometryMap, TableRowGeometry, ReportTemplate, BlockRegistry, Theme, DataSource, ReportData, RenderMode, ResolvedDocument, PagedDocument, PageGeometry, FormatContext, LayoutWarning } from '@broadpaper/core';
|
|
2
|
+
|
|
3
|
+
/** Creates a DOM node from a VNode. Attribute values are set with setAttribute (never innerHTML). */
|
|
4
|
+
declare function vnodeToDom(v: VNode | string, doc?: Document): Node;
|
|
5
|
+
/** Replaces the children of `container` with the given VNodes. */
|
|
6
|
+
declare function mountVNodes(container: HTMLElement, vnodes: Array<VNode | string>): void;
|
|
7
|
+
/** Injects (or updates) a style element with the given id. */
|
|
8
|
+
declare function ensureStyle(doc: Document, id: string, css: string): HTMLStyleElement;
|
|
9
|
+
|
|
10
|
+
/** FNV-1a 32-bit hash; fast and good enough for cache keys. */
|
|
11
|
+
declare function hashString(s: string): string;
|
|
12
|
+
declare function sectionHash(section: ResolvedNode, width: number, mode: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Measures resolved sections by laying them out as continuous galleys in a
|
|
15
|
+
* hidden container and reading geometry from the DOM. Results are cached by
|
|
16
|
+
* section content hash so unchanged sections are never re-measured.
|
|
17
|
+
*/
|
|
18
|
+
declare class DomMeasurer implements Measurer {
|
|
19
|
+
private readonly doc;
|
|
20
|
+
private readonly host;
|
|
21
|
+
private readonly cache;
|
|
22
|
+
private opts;
|
|
23
|
+
constructor(doc: Document, container?: HTMLElement);
|
|
24
|
+
setOptions(opts: RenderTreeOptions): void;
|
|
25
|
+
measure(sections: ResolvedNode[], width: number): GeometryMap;
|
|
26
|
+
clearCache(): void;
|
|
27
|
+
destroy(): void;
|
|
28
|
+
}
|
|
29
|
+
/** Returns the y coordinate (relative to `baseTop`) of the bottom of each line box. */
|
|
30
|
+
declare function readLineBoxes(el: HTMLElement, baseTop: number, nodeBottom: number): number[];
|
|
31
|
+
declare function readTableRows(el: HTMLElement, baseTop: number): TableRowGeometry[];
|
|
32
|
+
|
|
33
|
+
interface LayoutInput {
|
|
34
|
+
template: ReportTemplate;
|
|
35
|
+
registry: BlockRegistry;
|
|
36
|
+
theme?: Partial<Theme>;
|
|
37
|
+
dataSources?: DataSource[];
|
|
38
|
+
data?: ReportData;
|
|
39
|
+
sampleData?: ReportData;
|
|
40
|
+
mode?: RenderMode;
|
|
41
|
+
now?: Date;
|
|
42
|
+
locale?: string;
|
|
43
|
+
currency?: string;
|
|
44
|
+
timeZone?: string;
|
|
45
|
+
designIterations?: number;
|
|
46
|
+
allowStyleOverrides?: boolean;
|
|
47
|
+
/** Minimum pages to show (design canvas). */
|
|
48
|
+
minPages?: number;
|
|
49
|
+
/** Editor hook to decorate node wrappers. */
|
|
50
|
+
decorate?: RenderTreeOptions["decorate"];
|
|
51
|
+
}
|
|
52
|
+
interface LayoutResult {
|
|
53
|
+
resolved: ResolvedDocument;
|
|
54
|
+
paged: PagedDocument;
|
|
55
|
+
geometry: GeometryMap;
|
|
56
|
+
regionGeometry: GeometryMap;
|
|
57
|
+
page: PageGeometry;
|
|
58
|
+
theme: Theme;
|
|
59
|
+
format: FormatContext;
|
|
60
|
+
mode: RenderMode;
|
|
61
|
+
warnings: Array<{
|
|
62
|
+
source: "resolve" | "layout";
|
|
63
|
+
message: string;
|
|
64
|
+
nodeId?: string;
|
|
65
|
+
page?: number;
|
|
66
|
+
code?: LayoutWarning["code"];
|
|
67
|
+
}>;
|
|
68
|
+
/** Map of resolved node key → node, across all regions. */
|
|
69
|
+
nodesByKey: Map<string, ResolvedNode>;
|
|
70
|
+
/** Renders one page to a VNode. */
|
|
71
|
+
renderPage(index: number): VNode;
|
|
72
|
+
/** Renders every page. */
|
|
73
|
+
renderPages(): VNode[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A measurer whose results arrive asynchronously.
|
|
77
|
+
*
|
|
78
|
+
* The DOM measurer answers immediately because the browser has already done
|
|
79
|
+
* the layout; an engine-backed measurer has to run the engine first. Both
|
|
80
|
+
* drive the identical pipeline — only the awaiting differs.
|
|
81
|
+
*/
|
|
82
|
+
interface AsyncMeasurer {
|
|
83
|
+
measure(sections: ResolvedNode[], width: number): Promise<GeometryMap>;
|
|
84
|
+
setOptions?(opts: RenderTreeOptions): void;
|
|
85
|
+
}
|
|
86
|
+
/** Lays out a document with a measurer that answers immediately. */
|
|
87
|
+
declare function layoutDocument(input: LayoutInput, measurer: Measurer & {
|
|
88
|
+
setOptions?(opts: RenderTreeOptions): void;
|
|
89
|
+
}): LayoutResult;
|
|
90
|
+
/**
|
|
91
|
+
* Lays out a document with a measurer that answers asynchronously.
|
|
92
|
+
*
|
|
93
|
+
* This is what lets the editor page a document exactly as the PDF backend
|
|
94
|
+
* will: the same engine measures both, so a break in the canvas is a break in
|
|
95
|
+
* the file. Everything after measurement — pagination, composition, warnings —
|
|
96
|
+
* is the code the synchronous path runs.
|
|
97
|
+
*/
|
|
98
|
+
declare function layoutDocumentAsync(input: LayoutInput, measurer: AsyncMeasurer): Promise<LayoutResult>;
|
|
99
|
+
declare function composePage(index: number, number: number, total: number, first: boolean, resolved: ResolvedDocument, paged: PagedDocument, page: PageGeometry, opts: RenderTreeOptions, nodesByKey: Map<string, ResolvedNode>, regionGeometry: GeometryMap, theme: Theme, pageBackground?: string, geometry?: GeometryMap): VNode;
|
|
100
|
+
|
|
101
|
+
interface PreviewOptions extends LayoutInput {
|
|
102
|
+
/** Gap between pages in px. Default 24. */
|
|
103
|
+
pageGap?: number;
|
|
104
|
+
/** Scale factor. Default 1. */
|
|
105
|
+
zoom?: number;
|
|
106
|
+
/** Additional CSS class on the root. */
|
|
107
|
+
className?: string;
|
|
108
|
+
}
|
|
109
|
+
interface ReportPreview {
|
|
110
|
+
/** Re-renders with new input (template, data, theme, ...). */
|
|
111
|
+
update(input: Partial<PreviewOptions>): Promise<LayoutResult>;
|
|
112
|
+
/** The last layout result. */
|
|
113
|
+
result: LayoutResult | null;
|
|
114
|
+
/** Scroll to a page (1-based). */
|
|
115
|
+
goToPage(number: number): void;
|
|
116
|
+
destroy(): void;
|
|
117
|
+
}
|
|
118
|
+
/** Waits for web fonts so measurement is stable. Resolves immediately when unsupported. */
|
|
119
|
+
declare function waitForFonts(doc: Document, families?: string[]): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Mounts a live, paginated preview into a host element. This is the same
|
|
122
|
+
* pipeline the editor canvas and the PDF service use.
|
|
123
|
+
*/
|
|
124
|
+
declare function createReportPreview(container: HTMLElement, options: PreviewOptions): ReportPreview;
|
|
125
|
+
interface HtmlDocumentOptions {
|
|
126
|
+
title?: string;
|
|
127
|
+
/** Extra CSS appended after the document stylesheet. */
|
|
128
|
+
extraCss?: string;
|
|
129
|
+
/** Include the design-mode class (rarely wanted for PDFs). */
|
|
130
|
+
design?: boolean;
|
|
131
|
+
/** Document language for the html element. */
|
|
132
|
+
lang?: string;
|
|
133
|
+
/** PDF metadata. */
|
|
134
|
+
author?: string;
|
|
135
|
+
subject?: string;
|
|
136
|
+
keywords?: string[];
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Serialises a laid-out document to a standalone HTML page: one fixed-size box
|
|
140
|
+
* per page with `@page` sized to match. This is what the PDF service prints.
|
|
141
|
+
*/
|
|
142
|
+
declare function documentToHtml(result: LayoutResult, opts?: HtmlDocumentOptions): string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Document stylesheet shared by the editor canvas, the preview and the PDF
|
|
146
|
+
* service. Everything is scoped under `.bp-doc` so it never leaks into a host
|
|
147
|
+
* application. Block-specific visual styling is inline (resolved from the theme);
|
|
148
|
+
* this sheet provides structure, print rules and design-mode affordances.
|
|
149
|
+
*/
|
|
150
|
+
declare const DOCUMENT_CSS = "\n.bp-doc, .bp-doc * { box-sizing: border-box; }\n.bp-doc { -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; font-variant-numeric: tabular-nums; }\n.bp-doc p { margin: 0; }\n.bp-doc h1, .bp-doc h2, .bp-doc h3, .bp-doc h4 { margin: 0; font-weight: inherit; }\n.bp-doc img { max-width: 100%; }\n.bp-doc a { color: inherit; }\n.bp-doc table { border-spacing: 0; }\n.bp-doc ul.bp-list, .bp-doc ol.bp-list { margin: 0; padding-left: 1.4em; }\n.bp-doc .bp-list li { margin: 0.15em 0; }\n.bp-doc .bp-code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; background: rgba(0,0,0,0.05); padding: 0 3px; border-radius: 3px; }\n.bp-doc .bp-p--empty { min-height: 1em; }\n\n.bp-page { position: relative; overflow: hidden; background: #fff; color: #1a1d21; }\n.bp-page__region { position: absolute; overflow: hidden; }\n.bp-page__body { position: absolute; overflow: hidden; }\n.bp-fragment { position: absolute; left: 0; width: 100%; overflow: hidden; }\n.bp-fragment__clip { position: absolute; left: 0; right: 0; overflow: hidden; }\n.bp-repeat-header { position: absolute; top: 0; overflow: hidden; }\n.bp-repeat-header .bp-table { table-layout: fixed; }\n\n.bp-stack { display: flex; flex-direction: column; }\n.bp-stack > .bp-node { min-width: 0; }\n.bp-node--hidden > * { opacity: 0.4; }\n.bp-node--placeholder { opacity: 0.85; }\n.bp-empty-section, .bp-empty-column { display: none; }\n.bp-galley { position: absolute; left: -100000px; top: 0; visibility: hidden; pointer-events: none; }\n\n.bp-table { width: 100%; border-collapse: collapse; }\n.bp-table td, .bp-table th { word-wrap: break-word; overflow-wrap: anywhere; }\n.bp-chart__svg text { user-select: none; }\n.bp-page-break--print { height: 0; }\n\n/* --- design mode affordances (only when .bp-doc--design) --- */\n.bp-doc--design .bp-token { display: inline; padding: 0 5px; margin: 0 1px; border-radius: 4px; background: rgba(79, 111, 165, 0.14); color: #2f4f7f; font-size: 0.92em; font-weight: 500; white-space: nowrap; box-decoration-break: clone; -webkit-box-decoration-break: clone; }\n.bp-doc--design .bp-token--lg { font-size: 0.65em; vertical-align: middle; }\n.bp-doc--design .bp-token__value { opacity: 0.65; margin-left: 4px; font-weight: 400; }\n.bp-doc--design .bp-cell--bound { text-decoration: underline dotted rgba(79,111,165,0.45); text-underline-offset: 3px; text-decoration-thickness: 1px; }\n.bp-doc--design .bp-placeholder-text { color: #9aa1ab; font-style: italic; }\n.bp-doc--design .bp-empty-section { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 96px; border: 1.5px dashed #cfd5dd; border-radius: 8px; color: #6b7280; font-size: 12px; font-family: Inter, system-ui, sans-serif; background: #fafbfc; }\n.bp-doc--design .bp-empty-section__title { font-weight: 600; color: #4b5563; }\n.bp-doc--design .bp-empty-column { display: flex; align-items: center; justify-content: center; min-height: 56px; border: 1.5px dashed #d4d9e0; border-radius: 6px; color: #9aa1ab; font-size: 11px; font-family: Inter, system-ui, sans-serif; }\n.bp-doc--design .bp-spacer--design { background: repeating-linear-gradient(135deg, transparent 0 6px, rgba(0,0,0,0.04) 6px 8px); border-radius: 3px; }\n.bp-doc--design .bp-page-break { display: flex; align-items: center; gap: 8px; margin: 2px 0; color: #b4541f; font-size: 9.5px; font-family: Inter, system-ui, sans-serif; text-transform: uppercase; letter-spacing: 0.06em; }\n.bp-doc--design .bp-page-break::before, .bp-doc--design .bp-page-break::after { content: \"\"; flex: 1; border-top: 2px dashed rgba(180, 84, 31, 0.5); }\n.bp-doc--design .bp-page-break__label { display: inline-flex; align-items: center; padding: 1px 8px; border-radius: 999px; font-weight: 600; background: rgba(180, 84, 31, 0.12); white-space: nowrap; }\n\n/* Structural blocks draw nothing of their own, so design mode outlines them.\n The label is absolutely positioned: naming a box must not resize it. */\n.bp-doc--design .bp-container,\n.bp-doc--design .bp-keep-together { position: relative; outline: 1px dashed rgba(79, 111, 165, 0.32); outline-offset: 1px; border-radius: 3px; }\n.bp-doc--design .bp-container::before,\n.bp-doc--design .bp-keep-together::before {\n content: \"Container\"; position: absolute; top: -6px; left: 8px; z-index: 1; padding: 0 4px;\n background: #fff; color: #7c8697; font-family: Inter, system-ui, sans-serif; font-size: 8.5px;\n font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; pointer-events: none;\n}\n.bp-doc--design .bp-keep-together::before { content: \"Keep together\"; }\n.bp-doc--design .bp-repeater__badge, .bp-doc--design .bp-conditional__badge, .bp-doc--design .bp-table__badge, .bp-doc--design .bp-chart__badge { display: inline-flex; align-items: center; gap: 4px; margin-bottom: 4px; padding: 1px 7px; border-radius: 999px; font-size: 9.5px; font-weight: 600; letter-spacing: 0.02em; font-family: Inter, system-ui, sans-serif; background: rgba(79,111,165,0.12); color: #2f4f7f; user-select: none; }\n.bp-doc--design .bp-conditional--false .bp-conditional__badge { background: rgba(199,125,10,0.14); color: #8a5a06; }\n.bp-doc--design .bp-conditional--false > .bp-stack { opacity: 0.45; }\n.bp-doc--design .bp-repeater { border-left: 2px dashed rgba(79,111,165,0.35); padding-left: 8px; }\n.bp-doc--design .bp-conditional { border-left: 2px dashed rgba(199,125,10,0.45); padding-left: 8px; }\n.bp-doc--design .bp-image-placeholder { display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #f1f3f6, #e6e9ee); color: #6b7280; font-size: 11px; font-family: Inter, system-ui, sans-serif; border: 1px dashed #cfd5dd; }\n.bp-doc--design .bp-logo-placeholder { display: inline-flex; align-items: center; padding: 0 12px; border: 1px dashed #cfd5dd; border-radius: 4px; color: #6b7280; font-size: 11px; font-family: Inter, system-ui, sans-serif; }\n.bp-doc--design .bp-node--locked { outline: 1px dashed rgba(0,0,0,0.15); }\n\n@media print {\n .bp-page { break-after: page; page-break-after: always; box-shadow: none !important; margin: 0 !important; }\n .bp-page:last-child { break-after: auto; page-break-after: auto; }\n}\n";
|
|
151
|
+
/** Builds @font-face rules for a theme's fonts. */
|
|
152
|
+
declare function fontFaceCss(theme: Theme): string;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Charts you can point at, without changing what gets printed.
|
|
156
|
+
*
|
|
157
|
+
* The obvious way to get interactive charts is to draw them a second time with
|
|
158
|
+
* a charting library on screen. That gives you two chart implementations that
|
|
159
|
+
* drift, and a screen that no longer matches the file — which is the one thing
|
|
160
|
+
* this product is for. So the drawing does not change at all: the same SVG the
|
|
161
|
+
* PDF contains is the SVG the browser shows, and this adds a hover, a focus
|
|
162
|
+
* ring and a tooltip on top of the marks that are already there.
|
|
163
|
+
*
|
|
164
|
+
* The chart block records what each mark stands for as it draws
|
|
165
|
+
* (`data-bp-chart` on the wrapper, `data-bp-hit="<index>"` on each mark), so
|
|
166
|
+
* nothing here re-derives any geometry: it reads the real element's own box.
|
|
167
|
+
* The PDF translator strips those attributes, so print output is unchanged.
|
|
168
|
+
*
|
|
169
|
+
* Everything it adds is inert: no layout is read or written that could affect
|
|
170
|
+
* pagination, and `destroy()` puts the DOM back exactly as it found it.
|
|
171
|
+
*/
|
|
172
|
+
/** One mark a reader can point at. Mirrors `ChartHit` in the blocks package. */
|
|
173
|
+
interface Hit {
|
|
174
|
+
categoryIndex: number;
|
|
175
|
+
category: string;
|
|
176
|
+
seriesIndex: number;
|
|
177
|
+
series: string;
|
|
178
|
+
value: number | null;
|
|
179
|
+
formatted: string;
|
|
180
|
+
color: string;
|
|
181
|
+
share?: number;
|
|
182
|
+
}
|
|
183
|
+
interface ChartPayload {
|
|
184
|
+
type: string;
|
|
185
|
+
title: string;
|
|
186
|
+
hits: Hit[];
|
|
187
|
+
}
|
|
188
|
+
interface ChartInteractionOptions {
|
|
189
|
+
/**
|
|
190
|
+
* Builds the tooltip's content. Return a string (shown as text) or an
|
|
191
|
+
* element. The default names the category, the series and the value.
|
|
192
|
+
*/
|
|
193
|
+
tooltip?(hit: Hit, context: {
|
|
194
|
+
type: string;
|
|
195
|
+
title: string;
|
|
196
|
+
siblings: Hit[];
|
|
197
|
+
}): string | HTMLElement;
|
|
198
|
+
/** Called when a mark is clicked or activated from the keyboard. */
|
|
199
|
+
onSelect?(hit: Hit, event: Event): void;
|
|
200
|
+
/**
|
|
201
|
+
* Replaces the whole thing for one chart. Return `false` to leave that chart
|
|
202
|
+
* alone — the escape hatch for a host that would rather mount its own
|
|
203
|
+
* charting library over the box. The SVG underneath is still what prints.
|
|
204
|
+
*/
|
|
205
|
+
onChart?(element: HTMLElement, payload: ChartPayload): boolean | void;
|
|
206
|
+
/** Where the tooltip is appended. Defaults to the document body. */
|
|
207
|
+
portal?: HTMLElement;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Makes every chart under `root` respond to a pointer and to the keyboard.
|
|
211
|
+
*
|
|
212
|
+
* Safe to call again after the DOM is replaced — each call returns its own
|
|
213
|
+
* teardown, and calling it twice on the same nodes does not double up.
|
|
214
|
+
*/
|
|
215
|
+
declare function enableChartInteractions(root: HTMLElement, options?: ChartInteractionOptions): () => void;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Browser-side export helpers: a zero-infrastructure "Print / Save as PDF"
|
|
219
|
+
* fallback and a tiny client for the render service.
|
|
220
|
+
*
|
|
221
|
+
* They live here rather than in `@broadpaper/pdf` because they are pure browser
|
|
222
|
+
* code with no Chromium in them, and a designer that imported them from the
|
|
223
|
+
* Chromium backend package made every browser application install
|
|
224
|
+
* `playwright-core` — 14 MB of driver it can never run. `@broadpaper/pdf/client`
|
|
225
|
+
* re-exports this module, so the old import path still works.
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
interface PrintOptions {
|
|
229
|
+
title?: string;
|
|
230
|
+
/** Called after the print dialog closes (best effort). */
|
|
231
|
+
onDone?: () => void;
|
|
232
|
+
/** A licence for this print only, overriding whatever `configure` was given. */
|
|
233
|
+
license?: string | null;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Opens the laid-out document in a hidden iframe and triggers the browser's
|
|
237
|
+
* print dialog. Because the pages are fixed boxes, the output matches the
|
|
238
|
+
* server-rendered PDF (subject to the browser's own PDF writer).
|
|
239
|
+
*
|
|
240
|
+
* "Save as PDF" in that dialog produces a PDF as surely as either backend does,
|
|
241
|
+
* so it carries the evaluation mark on the same terms. The licence check is
|
|
242
|
+
* what makes this asynchronous; the returned promise settles when the dialog
|
|
243
|
+
* has been opened, and ignoring it is fine.
|
|
244
|
+
*/
|
|
245
|
+
declare function printReport(result: LayoutResult, opts?: PrintOptions): Promise<void>;
|
|
246
|
+
interface PdfClientOptions {
|
|
247
|
+
/** Base URL of the PDF service, e.g. "https://pdf.internal.example.com". */
|
|
248
|
+
baseUrl: string;
|
|
249
|
+
token?: string;
|
|
250
|
+
fetch?: typeof fetch;
|
|
251
|
+
}
|
|
252
|
+
interface PdfRequest {
|
|
253
|
+
template: ReportTemplate;
|
|
254
|
+
data?: ReportData;
|
|
255
|
+
theme?: Partial<Theme>;
|
|
256
|
+
dataSources?: DataSource[];
|
|
257
|
+
now?: Date;
|
|
258
|
+
locale?: string;
|
|
259
|
+
currency?: string;
|
|
260
|
+
timeZone?: string;
|
|
261
|
+
metadata?: {
|
|
262
|
+
title?: string;
|
|
263
|
+
author?: string;
|
|
264
|
+
subject?: string;
|
|
265
|
+
keywords?: string[];
|
|
266
|
+
};
|
|
267
|
+
network?: {
|
|
268
|
+
allowedHosts?: string[];
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/** Minimal client for the BroadPaper PDF service. */
|
|
272
|
+
declare function createPdfClient(opts: PdfClientOptions): {
|
|
273
|
+
render(req: PdfRequest): Promise<{
|
|
274
|
+
blob: Blob;
|
|
275
|
+
pages: number;
|
|
276
|
+
warnings: number;
|
|
277
|
+
}>;
|
|
278
|
+
health(): Promise<boolean>;
|
|
279
|
+
};
|
|
280
|
+
/** Triggers a download of a Blob in the browser. */
|
|
281
|
+
declare function downloadBlob(blob: Blob, filename: string): void;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* The report as a web page: read-only, page-shaped, and interactive where a PDF
|
|
285
|
+
* cannot be.
|
|
286
|
+
*
|
|
287
|
+
* This is the other half of the product. A saved template plus data is a file,
|
|
288
|
+
* and it is also something a person should be able to *read* — in a tab, on a
|
|
289
|
+
* phone, shared by link — without downloading anything. The drawing is
|
|
290
|
+
* identical either way: the same paginator decides the breaks, the same blocks
|
|
291
|
+
* draw the marks, the same SVG the PDF contains is the SVG the browser shows.
|
|
292
|
+
* What differs is only what a screen can do that paper cannot, which is why
|
|
293
|
+
* chart interactions are on by default here and stripped on the way to the file.
|
|
294
|
+
*
|
|
295
|
+
* It is deliberately a thin layer over `createReportPreview`. Everything about
|
|
296
|
+
* measurement, pagination and rendering is that pipeline's job and must stay
|
|
297
|
+
* there, or the viewer and the file start to disagree — which is the one thing
|
|
298
|
+
* this product exists to prevent. What is added here is presentation and
|
|
299
|
+
* navigation: paper on a ground, a fit-to-width zoom that responds to the
|
|
300
|
+
* element's own width, page navigation, and an optional toolbar.
|
|
301
|
+
*/
|
|
302
|
+
|
|
303
|
+
/** How the viewer sizes the page against the space it has. */
|
|
304
|
+
type ViewerFit = "width" | "page" | "actual";
|
|
305
|
+
interface ReportViewerOptions extends Omit<PreviewOptions, "mode" | "zoom"> {
|
|
306
|
+
/**
|
|
307
|
+
* How to size pages. `"width"` (the default) fits the page to the element,
|
|
308
|
+
* never enlarging past 100% — a report blown up to 180% on a wide monitor
|
|
309
|
+
* looks like a mistake, and the text is already sized for print. `"page"`
|
|
310
|
+
* fits a whole page in view; `"actual"` pins it to 100%.
|
|
311
|
+
*/
|
|
312
|
+
fit?: ViewerFit;
|
|
313
|
+
/** A fixed zoom, which overrides `fit`. 1 is actual size. */
|
|
314
|
+
zoom?: number;
|
|
315
|
+
/** Gap between pages, in CSS px at 100%. Default 32. */
|
|
316
|
+
pageGap?: number;
|
|
317
|
+
/**
|
|
318
|
+
* The toolbar: page position, zoom and print. `false` hides it, for a host
|
|
319
|
+
* that would rather draw its own chrome around `goToPage` and `setZoom`.
|
|
320
|
+
* Default true.
|
|
321
|
+
*/
|
|
322
|
+
toolbar?: boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Pointing at a chart mark shows what it stands for. Default true — it is
|
|
325
|
+
* most of the reason to read a report on a screen rather than on paper.
|
|
326
|
+
* `false`, or options passed straight to `enableChartInteractions`.
|
|
327
|
+
*/
|
|
328
|
+
interactions?: boolean | ChartInteractionOptions;
|
|
329
|
+
/** Extra class on the viewer root. */
|
|
330
|
+
className?: string;
|
|
331
|
+
/** Accessible label for the scroll region. Default "Report". */
|
|
332
|
+
label?: string;
|
|
333
|
+
/** Called whenever the visible page changes. */
|
|
334
|
+
onPageChange?(page: number): void;
|
|
335
|
+
/** Called after each layout, with the result. */
|
|
336
|
+
onLayout?(result: LayoutResult): void;
|
|
337
|
+
/**
|
|
338
|
+
* What the toolbar's print button does. The default opens the browser's
|
|
339
|
+
* print dialog on the page as laid out, which produces a file matching this
|
|
340
|
+
* view. Supply your own to hand the reader a real PDF instead.
|
|
341
|
+
*/
|
|
342
|
+
onPrint?(result: LayoutResult): void;
|
|
343
|
+
}
|
|
344
|
+
interface ReportViewer {
|
|
345
|
+
/** Re-renders with new input. Resolves with the new layout. */
|
|
346
|
+
update(input: Partial<ReportViewerOptions>): Promise<LayoutResult>;
|
|
347
|
+
/** The last layout result, or null before the first render. */
|
|
348
|
+
readonly result: LayoutResult | null;
|
|
349
|
+
/** The page currently in view, 1-based. */
|
|
350
|
+
readonly page: number;
|
|
351
|
+
/** The number of pages, or 0 before the first render. */
|
|
352
|
+
readonly pageCount: number;
|
|
353
|
+
/** Scrolls to a page, 1-based and clamped. */
|
|
354
|
+
goToPage(page: number): void;
|
|
355
|
+
/** Sets an explicit zoom, or returns to a fit mode. */
|
|
356
|
+
setZoom(zoom: number | ViewerFit): void;
|
|
357
|
+
/** The zoom actually in use, resolved from the fit mode. */
|
|
358
|
+
readonly zoom: number;
|
|
359
|
+
/** Opens the print dialog (or calls `onPrint`). */
|
|
360
|
+
print(): void;
|
|
361
|
+
destroy(): void;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Mounts a read-only, page-shaped view of a template into an element.
|
|
365
|
+
*
|
|
366
|
+
* The container is expected to have a height — the viewer fills it and scrolls
|
|
367
|
+
* inside itself, so that the toolbar stays put and `fit: "width"` has something
|
|
368
|
+
* to measure against. A container with no height collapses to the toolbar.
|
|
369
|
+
*/
|
|
370
|
+
declare function createReportViewer(container: HTMLElement, options: ReportViewerOptions): ReportViewer;
|
|
371
|
+
|
|
372
|
+
export { type AsyncMeasurer, type ChartInteractionOptions, DOCUMENT_CSS, DomMeasurer, type HtmlDocumentOptions, type LayoutInput, type LayoutResult, type PdfClientOptions, type PdfRequest, type PreviewOptions, type PrintOptions, type ReportPreview, type ReportViewer, type ReportViewerOptions, type ViewerFit, composePage, createPdfClient, createReportPreview, createReportViewer, documentToHtml, downloadBlob, enableChartInteractions, ensureStyle, fontFaceCss, hashString, layoutDocument, layoutDocumentAsync, mountVNodes, printReport, readLineBoxes, readTableRows, sectionHash, vnodeToDom, waitForFonts };
|