@quario/viewer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/LICENSE +219 -0
- package/README.md +303 -0
- package/lib/button.js +88 -0
- package/lib/check.js +154 -0
- package/lib/chrome.js +187 -0
- package/lib/index.d.ts +130 -0
- package/lib/index.js +450 -0
- package/lib/mark.js +40 -0
- package/lib/menu.js +318 -0
- package/lib/panel.js +126 -0
- package/lib/register.d.ts +9 -0
- package/lib/register.js +10 -0
- package/lib/stage.js +361 -0
- package/lib/style.js +94 -0
- package/lib/toolbar.js +95 -0
- package/lib/zoom.js +45 -0
- package/package.json +78 -0
package/lib/check.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the host got wrong, named. The element's own mistakes — a report that
|
|
3
|
+
* is not compiled, a missing html target, a malformed option property — are
|
|
4
|
+
* definition-shaped: they describe the embedding, not the report, so each
|
|
5
|
+
* check here throws a `TypeError` naming the property and the expectation.
|
|
6
|
+
* The element rethrows them from its render task, which is how one channel
|
|
7
|
+
* (the error event and the panel) carries every failure a host can cause.
|
|
8
|
+
*
|
|
9
|
+
* Pure policy, no DOM: this module is what the Node suite pins.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { STEPS } from "./zoom.js";
|
|
13
|
+
|
|
14
|
+
/** @type {(message: string) => never} */
|
|
15
|
+
let fail = (message) => {
|
|
16
|
+
throw new TypeError("quario-viewer: " + message);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// The PdfPage vocabulary and defaults, mirrored from the pdf target: the
|
|
20
|
+
// sheet is a visual stand-in for the page the host configured there, so the
|
|
21
|
+
// same values must mean the same geometry.
|
|
22
|
+
/** @type {Record<string, [number, number]>} */
|
|
23
|
+
let SIZES = { A4: [595.28, 841.89], letter: [612, 792] };
|
|
24
|
+
|
|
25
|
+
let REPORT = [
|
|
26
|
+
(/** @type {any} */ report) => report && typeof report === "object",
|
|
27
|
+
(/** @type {any} */ report) => typeof report.stream === "function",
|
|
28
|
+
(/** @type {any} */ report) => typeof report.render === "function",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** @type {(report: any) => boolean} */
|
|
32
|
+
let compiled = (report) => REPORT.every((check) => check(report));
|
|
33
|
+
|
|
34
|
+
/** @type {(target: any) => boolean} */
|
|
35
|
+
let htmlTarget = (target) => target?.name === "html";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The target the sheet displays: the one named `"html"`, checked along with
|
|
39
|
+
* the shapes around it.
|
|
40
|
+
*
|
|
41
|
+
* @param {any} report
|
|
42
|
+
* @param {any} targets
|
|
43
|
+
* @returns {any} The html target.
|
|
44
|
+
*/
|
|
45
|
+
export let display = (report, targets) => {
|
|
46
|
+
if (!compiled(report)) fail("report: expected a compiled report from a quario instance");
|
|
47
|
+
if (!Array.isArray(targets)) fail("targets: expected an array of render targets");
|
|
48
|
+
let found = targets.find(htmlTarget);
|
|
49
|
+
if (!found) fail('targets: no "html" target — include html() to display');
|
|
50
|
+
return found;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** @type {(size: unknown) => [number, number] | false} */
|
|
54
|
+
let named = (size) =>
|
|
55
|
+
Object.hasOwn(SIZES, /** @type {string} */ (size)) && SIZES[/** @type {string} */ (size)];
|
|
56
|
+
|
|
57
|
+
/** @type {(size: unknown) => any} */
|
|
58
|
+
let measure = (size) => (Array.isArray(size) ? size : named(size));
|
|
59
|
+
|
|
60
|
+
/** @type {(n: number) => boolean} */
|
|
61
|
+
let positive = (n) => Number.isFinite(n) && n > 0;
|
|
62
|
+
|
|
63
|
+
/** @type {(dimensions: any) => { width: number, height: number }} */
|
|
64
|
+
let extents = (dimensions) => {
|
|
65
|
+
let width = +dimensions[0];
|
|
66
|
+
let height = +dimensions[1];
|
|
67
|
+
if (!positive(width) || !positive(height)) fail("page.size: expected finite positive dimensions");
|
|
68
|
+
return { width, height };
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** @type {(margin: number, width: number, height: number) => boolean} */
|
|
72
|
+
let fits = (margin, width, height) =>
|
|
73
|
+
[Number.isFinite(margin), margin >= 0, 2 * margin < Math.min(width, height)].every(Boolean);
|
|
74
|
+
|
|
75
|
+
/** @type {(page: any, width: number, height: number) => number} */
|
|
76
|
+
let inset = (page, width, height) => {
|
|
77
|
+
let margin = page?.margin ?? 54;
|
|
78
|
+
if (!fits(margin, width, height))
|
|
79
|
+
fail("page.margin: expected a non-negative number smaller than half the page");
|
|
80
|
+
return margin;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The sheet's geometry from the `page` property. Width and margin size the
|
|
85
|
+
* sheet; height is the stamp period for the unlicensed watermark (full page
|
|
86
|
+
* box, same as the PDF target — margin does not shrink it).
|
|
87
|
+
*
|
|
88
|
+
* @param {any} page
|
|
89
|
+
* @returns {{ width: number, height: number, margin: number }}
|
|
90
|
+
*/
|
|
91
|
+
export let geometry = (page) => {
|
|
92
|
+
let size = page?.size ?? "A4";
|
|
93
|
+
let dimensions = measure(size);
|
|
94
|
+
if (!dimensions) fail('page.size: unknown page size "' + size + '"');
|
|
95
|
+
let { width, height } = extents(dimensions);
|
|
96
|
+
return { width, height, margin: inset(page, width, height) };
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/** @type {(mode: any, floor: number, ceiling: number) => boolean} */
|
|
100
|
+
let inRange = (mode, floor, ceiling) =>
|
|
101
|
+
[Number.isFinite(mode), mode >= floor, mode <= ceiling].every(Boolean);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The zoom mode from the `zoom` property. Fit is the default: the host owns
|
|
105
|
+
* the box, and a report nobody can read until they touch a control is not
|
|
106
|
+
* much of a preview. A host may author any percentage from 25 to 200 —
|
|
107
|
+
* continuous, not one of the menu's own stops; fit itself computes anything
|
|
108
|
+
* at or below 100, so this deliberately validates less than the element can
|
|
109
|
+
* display.
|
|
110
|
+
*
|
|
111
|
+
* @param {any} zoom
|
|
112
|
+
* @returns {"fit" | number}
|
|
113
|
+
*/
|
|
114
|
+
export let level = (zoom) => {
|
|
115
|
+
let mode = zoom ?? "fit";
|
|
116
|
+
// The range is the menu's own ends, so the two cannot drift apart.
|
|
117
|
+
let [floor, ceiling] = [STEPS[0], STEPS[STEPS.length - 1]];
|
|
118
|
+
if (mode !== "fit" && !inRange(mode, floor, ceiling))
|
|
119
|
+
fail('zoom: expected "fit" or a percentage between ' + floor + " and " + ceiling);
|
|
120
|
+
return mode;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** @type {(base: unknown) => boolean} */
|
|
124
|
+
let nonempty = (base) => typeof base === "string" && Boolean(base);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The export base name from the `filename` property, extensionless.
|
|
128
|
+
*
|
|
129
|
+
* @param {any} filename
|
|
130
|
+
* @returns {string}
|
|
131
|
+
*/
|
|
132
|
+
export let name = (filename) => {
|
|
133
|
+
let base = filename ?? "report";
|
|
134
|
+
if (!nonempty(base)) fail("filename: expected a non-empty string");
|
|
135
|
+
return base;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/** @type {Record<string, string>} */
|
|
139
|
+
let SCHEME = { light: "light", dark: "dark", auto: "light dark" };
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The used `color-scheme` from the `colorScheme` property. Light is the
|
|
143
|
+
* default: embedding is the product, and following the OS would paint a light
|
|
144
|
+
* toolbar in a host that already chose dark. `"auto"` is the opt-in that
|
|
145
|
+
* follows, as CSS `light dark`.
|
|
146
|
+
*
|
|
147
|
+
* @param {any} colorScheme
|
|
148
|
+
* @returns {string}
|
|
149
|
+
*/
|
|
150
|
+
export let scheme = (colorScheme) => {
|
|
151
|
+
let pin = colorScheme ?? "light";
|
|
152
|
+
if (!Object.hasOwn(SCHEME, pin)) fail('colorScheme: expected "light", "dark", or "auto"');
|
|
153
|
+
return SCHEME[pin];
|
|
154
|
+
};
|
package/lib/chrome.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The element's chrome: the backdrop, the bar, and the render indicator on
|
|
3
|
+
* its edge, under stable `qv-*` class names. The sheet is not here —
|
|
4
|
+
* `stage.js` keeps it paper.
|
|
5
|
+
*
|
|
6
|
+
* This module also owns the **default palette**: the `:host` block below is
|
|
7
|
+
* the one place a `--qv-*` fallback is written, and every sheet — this one
|
|
8
|
+
* included — paints from the `--_*` alias it declares. That is what makes a
|
|
9
|
+
* color one edit rather than a hunt, and `viewer.test.js` holds both halves:
|
|
10
|
+
* `--qv-` appears in no other module, and the aliases read are exactly the
|
|
11
|
+
* ones declared. The fallbacks are `light-dark()`, so a `color-scheme` pin on
|
|
12
|
+
* the host paints chrome without a class.
|
|
13
|
+
*
|
|
14
|
+
* The cost is that the other sheets no longer stand alone — they paint only
|
|
15
|
+
* where this one is adopted too. It is presence, not order: a custom property
|
|
16
|
+
* resolves down the inherited chain, so where CHROME sits in `static styles`
|
|
17
|
+
* does not matter, only that it is there.
|
|
18
|
+
*
|
|
19
|
+
* Host tokens always win, and `:host` is what makes that survive here. A
|
|
20
|
+
* `:host { --qv-border: … }` *default* would carry pseudo-class specificity
|
|
21
|
+
* and beat a host's own `quario-viewer` rule — the seam ADR 0018 declines to
|
|
22
|
+
* make a lie. A `var()` fallback applies only when the token is unset, so it
|
|
23
|
+
* loses to a host token at any specificity. The private half inverts that on
|
|
24
|
+
* purpose: `:host` outranks that same host rule, so nothing outside can
|
|
25
|
+
* shadow an alias by accident.
|
|
26
|
+
*
|
|
27
|
+
* Two of the sixteen are the sheet's rather than the chrome's: a color scheme
|
|
28
|
+
* paints chrome only (ADR 0018), so `--qv-sheet-shadow` is the sheet's edge
|
|
29
|
+
* against the backdrop and `--qv-mark` is scheme-free by design. They are in
|
|
30
|
+
* the block because it has no exceptions, not because they follow the pin.
|
|
31
|
+
* `--qv-focus` and `--qv-progress` repeat the accent rather than chain a third
|
|
32
|
+
* alias — a host is free to move one alone.
|
|
33
|
+
*
|
|
34
|
+
* What is not here is what another module owns: `stage.js` the surface a
|
|
35
|
+
* report is scaled on, `panel.js` the error panel, `button.js` the controls
|
|
36
|
+
* every group in the bar is built from. The element composes their templates
|
|
37
|
+
* and stylesheets; the imports run one way only.
|
|
38
|
+
*/
|
|
39
|
+
import { css, html, nothing } from "lit";
|
|
40
|
+
|
|
41
|
+
export let CHROME = css`
|
|
42
|
+
/* The default palette. */
|
|
43
|
+
:host {
|
|
44
|
+
display: block;
|
|
45
|
+
color-scheme: light;
|
|
46
|
+
|
|
47
|
+
--_backdrop: var(--qv-backdrop, light-dark(#e9e9eb, #1c1c1e));
|
|
48
|
+
--_text: var(--qv-text, light-dark(#454548, #c7c7cc));
|
|
49
|
+
--_bar: var(--qv-bar, light-dark(#f8f8f9, #2c2c2e));
|
|
50
|
+
--_border: var(--qv-border, light-dark(#dededf, #3a3a3c));
|
|
51
|
+
|
|
52
|
+
--_icon: var(--qv-icon, light-dark(#58585c, #8e8e93));
|
|
53
|
+
--_icon-active: var(--qv-icon-active, light-dark(#212124, #f2f2f7));
|
|
54
|
+
--_hover: var(--qv-hover, light-dark(#ebebec, #3a3a3c));
|
|
55
|
+
--_active: var(--qv-active, light-dark(#e2e2e4, #48484a));
|
|
56
|
+
--_focus: var(--qv-focus, #4a90d9);
|
|
57
|
+
|
|
58
|
+
--_progress: var(--qv-progress, #4a90d9);
|
|
59
|
+
|
|
60
|
+
--_error: var(--qv-error, light-dark(#fdeded, #3b1c1c));
|
|
61
|
+
--_error-text: var(--qv-error-text, light-dark(#5f2120, #f5c6c6));
|
|
62
|
+
--_error-border: var(--qv-error-border, light-dark(#f1c5c5, #6b3434));
|
|
63
|
+
--_error-hover: var(--qv-error-hover, light-dark(#f7dcdc, #4a2626));
|
|
64
|
+
|
|
65
|
+
/* The sheet's, not the chrome's. */
|
|
66
|
+
--_sheet-shadow: var(
|
|
67
|
+
--qv-sheet-shadow,
|
|
68
|
+
0 1px 2px light-dark(rgba(0, 0, 0, 0.16), rgba(0, 0, 0, 0.5)),
|
|
69
|
+
0 6px 24px light-dark(rgba(0, 0, 0, 0.09), rgba(0, 0, 0, 0.45))
|
|
70
|
+
);
|
|
71
|
+
--_mark: var(--qv-mark, #999);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.qv-viewer {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
width: 100%;
|
|
78
|
+
height: 100%;
|
|
79
|
+
background: var(--_backdrop);
|
|
80
|
+
color: var(--_text);
|
|
81
|
+
font: 13px/1.4 system-ui, sans-serif;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Everything below the bar. It exists to be the box the error panel is
|
|
85
|
+
positioned against: anchored to the viewer instead, the panel would cover
|
|
86
|
+
the bar's own controls. */
|
|
87
|
+
.qv-body {
|
|
88
|
+
position: relative;
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-direction: column;
|
|
91
|
+
flex: 1;
|
|
92
|
+
min-height: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.qv-bar {
|
|
96
|
+
position: relative;
|
|
97
|
+
display: flex;
|
|
98
|
+
justify-content: flex-end;
|
|
99
|
+
gap: 2px;
|
|
100
|
+
flex: none;
|
|
101
|
+
padding: 5px 10px;
|
|
102
|
+
background: var(--_bar);
|
|
103
|
+
border-bottom: 1px solid var(--_border);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* The render indicator, lying over the bar's bottom border. It is positioned
|
|
107
|
+
rather than laid out for two reasons: nothing under it moves as renders come
|
|
108
|
+
and go, and it stays out of the width .qv-scroll reports, which the stage
|
|
109
|
+
fits against.
|
|
110
|
+
Showing is instant and hiding fades, which is the whole timing rule: a render
|
|
111
|
+
short enough for the fade to matter is one the reader never waited for. The
|
|
112
|
+
transition lives on the idle state alone, so the busy state has none to run
|
|
113
|
+
and the fade starts only on the way back. */
|
|
114
|
+
.qv-progress {
|
|
115
|
+
position: absolute;
|
|
116
|
+
inset: auto 0 -1px 0;
|
|
117
|
+
height: 2px;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
opacity: 0;
|
|
120
|
+
transition: opacity 200ms ease;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.qv-viewer[aria-busy="true"] .qv-progress {
|
|
124
|
+
opacity: 1;
|
|
125
|
+
transition: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* The accent is the same in both schemes: it has to read on the light bar
|
|
129
|
+
and the dark one, and a second hue would be a second opinion. */
|
|
130
|
+
.qv-progress::after {
|
|
131
|
+
content: "";
|
|
132
|
+
display: block;
|
|
133
|
+
width: 35%;
|
|
134
|
+
height: 100%;
|
|
135
|
+
background: var(--_progress);
|
|
136
|
+
animation: qv-slide 1.1s ease-in-out infinite;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@keyframes qv-slide {
|
|
140
|
+
from {
|
|
141
|
+
transform: translateX(-100%);
|
|
142
|
+
}
|
|
143
|
+
to {
|
|
144
|
+
transform: translateX(385%);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Motion is the only thing the strip says; without it, say the same with a
|
|
149
|
+
filled bar rather than nothing at all. */
|
|
150
|
+
@media (prefers-reduced-motion: reduce) {
|
|
151
|
+
.qv-progress::after {
|
|
152
|
+
width: 100%;
|
|
153
|
+
opacity: 0.55;
|
|
154
|
+
animation: none;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* The zoom group holds the left of the bar; the exports stay flush right.
|
|
159
|
+
Layout only: the menu inside it places itself against its own trigger, not
|
|
160
|
+
against this element. */
|
|
161
|
+
.qv-zoom {
|
|
162
|
+
display: flex;
|
|
163
|
+
margin-right: auto;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.qv-exports {
|
|
167
|
+
display: flex;
|
|
168
|
+
gap: 2px;
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The render indicator. Idle it stays out of the accessibility tree — an
|
|
174
|
+
* unstarted progressbar has nothing to announce — and the stylesheet above
|
|
175
|
+
* hooks on the viewer's `aria-busy` to show it.
|
|
176
|
+
*
|
|
177
|
+
* @param {boolean} busy
|
|
178
|
+
* @returns {import('lit').TemplateResult}
|
|
179
|
+
*/
|
|
180
|
+
export let progress = (busy) => html`
|
|
181
|
+
<div
|
|
182
|
+
class="qv-progress"
|
|
183
|
+
role="progressbar"
|
|
184
|
+
aria-label="Rendering report"
|
|
185
|
+
aria-hidden=${busy ? nothing : "true"}
|
|
186
|
+
></div>
|
|
187
|
+
`;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { LitElement } from "lit";
|
|
2
|
+
import type { CompiledReport, Target } from "quario";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sheet geometry, in PostScript points — the pdf target's `PdfPage`
|
|
6
|
+
* vocabulary, because the sheet stands in visually for the page configured
|
|
7
|
+
* there. Purely visual: pass the same values to `pdf({ page })` so preview
|
|
8
|
+
* and export agree.
|
|
9
|
+
*/
|
|
10
|
+
export interface ViewerPage {
|
|
11
|
+
/** Named size or `[width, height]` in points. Default `'A4'`. */
|
|
12
|
+
size?: "A4" | "letter" | [number, number];
|
|
13
|
+
/** Margin on all four sides, in points. Default `54` (0.75 in). */
|
|
14
|
+
margin?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A compiled report, from a quario instance's `report()`. The instance —
|
|
19
|
+
* and with it the license and the registry — stays the host's concern: the
|
|
20
|
+
* element takes the compiled artifact, never a schema.
|
|
21
|
+
*/
|
|
22
|
+
export type ViewableReport = CompiledReport;
|
|
23
|
+
|
|
24
|
+
/** Which failure an `error` event names. */
|
|
25
|
+
export type ViewerErrorKind = "mount-render" | "update-render" | "export";
|
|
26
|
+
|
|
27
|
+
/** The `error` event's payload. */
|
|
28
|
+
export interface ViewerErrorDetail {
|
|
29
|
+
/** The caught value, exactly as thrown. */
|
|
30
|
+
error: unknown;
|
|
31
|
+
/**
|
|
32
|
+
* Which failure occurred: `mount-render` until a render has ever reached
|
|
33
|
+
* the sheet, `update-render` after, `export` for a download that could not
|
|
34
|
+
* be produced.
|
|
35
|
+
*/
|
|
36
|
+
kind: ViewerErrorKind;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The embeddable report viewer, `<quario-viewer>`. Assign `report`,
|
|
41
|
+
* `targets` and `data` as properties (they are values no attribute could
|
|
42
|
+
* carry) and listen for `rendered` and `error`; rapid successive writes
|
|
43
|
+
* render only the newest state. Importing this module defines nothing —
|
|
44
|
+
* import `@quario/viewer/register` for the one-line define, or call
|
|
45
|
+
* `customElements.define` with a tag of your own.
|
|
46
|
+
*
|
|
47
|
+
* Failures split along one line: the host's own mistakes (a report that is
|
|
48
|
+
* not compiled, a missing `"html"` target, a malformed option property)
|
|
49
|
+
* throw a `TypeError` naming the property, delivered through the `error`
|
|
50
|
+
* event and the error panel like every other failure; a report that could
|
|
51
|
+
* not be rendered leaves the element up with the panel saying why. A
|
|
52
|
+
* superseded render's failure is reported to no one.
|
|
53
|
+
*
|
|
54
|
+
* Removing the element abandons in-flight work and releases its observers;
|
|
55
|
+
* the properties persist, and reconnecting re-renders from them. There is no
|
|
56
|
+
* `destroy()` — removal is destruction only in the garbage-collection sense,
|
|
57
|
+
* and reparenting is safe. An export settling after removal downloads
|
|
58
|
+
* nothing.
|
|
59
|
+
*/
|
|
60
|
+
export class QuarioViewer extends LitElement {
|
|
61
|
+
/** The compiled report to display and export. */
|
|
62
|
+
report: ViewableReport | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* The configured targets, as passed to `report.render` — one must be named
|
|
65
|
+
* `"html"` to display; ones named `"pdf"`/`"xlsx"`/`"csv"` become
|
|
66
|
+
* export buttons, in array order.
|
|
67
|
+
*/
|
|
68
|
+
targets: readonly Target[] | undefined;
|
|
69
|
+
/** The render document. Assigning re-renders; the newest write wins. */
|
|
70
|
+
data: unknown;
|
|
71
|
+
/**
|
|
72
|
+
* The zoom to open at: `'fit'` (the default) or a percentage. Fit sizes
|
|
73
|
+
* the sheet to the viewer's width and is shrink-only — it never enlarges
|
|
74
|
+
* past 100%, so a viewer with room to spare shows the report at its true
|
|
75
|
+
* point size. An authored percentage must be between 25 and 200, and is
|
|
76
|
+
* continuous in that range rather than one of the zoom menu's own stops;
|
|
77
|
+
* fit itself is unbounded below, so the viewer can display percentages
|
|
78
|
+
* this property would not accept. That asymmetry is deliberate: only fit
|
|
79
|
+
* computes them.
|
|
80
|
+
*/
|
|
81
|
+
zoom: "fit" | number | undefined;
|
|
82
|
+
/** Sheet geometry. The preview approximates; exports are exact. */
|
|
83
|
+
page: ViewerPage | undefined;
|
|
84
|
+
/** Export download name, without extension. Default `'report'`. */
|
|
85
|
+
filename: string | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Chrome color scheme. `'light'` (the default) and `'dark'` pin; `'auto'`
|
|
88
|
+
* follows the OS via CSS `color-scheme`. The sheet stays white.
|
|
89
|
+
*/
|
|
90
|
+
colorScheme: "light" | "dark" | "auto" | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* The newest render settling: `true` when it reached the sheet, `false`
|
|
93
|
+
* when it failed or there was nothing to render. Never rejects — failures
|
|
94
|
+
* are the `error` event's — and like every outcome here it answers for
|
|
95
|
+
* the newest render only.
|
|
96
|
+
*/
|
|
97
|
+
get renderComplete(): Promise<boolean>;
|
|
98
|
+
|
|
99
|
+
addEventListener<K extends keyof QuarioViewerEventMap>(
|
|
100
|
+
type: K,
|
|
101
|
+
listener: (this: QuarioViewer, event: QuarioViewerEventMap[K]) => void,
|
|
102
|
+
options?: boolean | AddEventListenerOptions,
|
|
103
|
+
): void;
|
|
104
|
+
addEventListener(
|
|
105
|
+
type: string,
|
|
106
|
+
listener: EventListenerOrEventListenerObject,
|
|
107
|
+
options?: boolean | AddEventListenerOptions,
|
|
108
|
+
): void;
|
|
109
|
+
removeEventListener<K extends keyof QuarioViewerEventMap>(
|
|
110
|
+
type: K,
|
|
111
|
+
listener: (this: QuarioViewer, event: QuarioViewerEventMap[K]) => void,
|
|
112
|
+
options?: boolean | EventListenerOptions,
|
|
113
|
+
): void;
|
|
114
|
+
removeEventListener(
|
|
115
|
+
type: string,
|
|
116
|
+
listener: EventListenerOrEventListenerObject,
|
|
117
|
+
options?: boolean | EventListenerOptions,
|
|
118
|
+
): void;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** The element's events. Both are non-bubbling, like `<img>`'s. */
|
|
122
|
+
export interface QuarioViewerEventMap {
|
|
123
|
+
/** A render reached the sheet — the mount and every update. */
|
|
124
|
+
rendered: CustomEvent<undefined>;
|
|
125
|
+
/** A failure the host should know about; `detail.kind` says which. */
|
|
126
|
+
error: CustomEvent<ViewerErrorDetail>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The tag `@quario/viewer/register` defines the element under. */
|
|
130
|
+
export const TAG: "quario-viewer";
|