@quario/viewer 0.7.0 → 0.9.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 +79 -49
- package/README.md +71 -58
- package/lib/icons.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/stage.js +49 -40
- package/lib/toolbar.js +1 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,70 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
# @quario/viewer
|
|
2
|
+
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- The viewer offers a Word download. Pass a `docx` target in `targets`, and the bar carries a `DOCX`
|
|
8
|
+
button beside the others. The buttons keep the order you gave. The file carries the content type
|
|
9
|
+
`application/vnd.openxmlformats-officedocument.wordprocessingml.document`.
|
|
10
|
+
|
|
11
|
+
Earlier versions dropped a `docx` target. The viewer rendered no button for it, and it reported no
|
|
12
|
+
failure.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- quario@0.9.0
|
|
18
|
+
- @quario/layout@0.6.0
|
|
19
|
+
- @quario/landing@0.2.1
|
|
20
|
+
|
|
21
|
+
## 0.8.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- **An image's box is now as wide as the container it sits in.** A
|
|
26
|
+
`background` or `border*` on an image item hugged the picture and now spans
|
|
27
|
+
the content width, or the slot's share inside a split; the picture itself
|
|
28
|
+
does not move. See the `@quario/layout` changelog for the rule and for what
|
|
29
|
+
it costs a report that relied on a border hugging a logo.
|
|
30
|
+
- **Only the pages you can see are on the sheet.** A page outside the reach
|
|
31
|
+
used to be an empty canvas holding its place; now it has no element at all,
|
|
32
|
+
and the sheet carries the document's extent itself — its height and width are
|
|
33
|
+
written from the layout, and it paints the page silhouettes. The stretch a
|
|
34
|
+
reader scrolls through is exactly what it was, and a scale change no longer
|
|
35
|
+
costs the browser a relayout of every page: that was around 10 microseconds a
|
|
36
|
+
page, so a five-thousand-page report paid roughly 50 ms of it each time.
|
|
37
|
+
|
|
38
|
+
This changes the sheet's markup, which matters if you style it. `.qv-page` is
|
|
39
|
+
now absolutely positioned inside `.qv-sheet`, and is present only for the pages
|
|
40
|
+
on screen and one viewport height either side. A rule that gave a page a
|
|
41
|
+
margin, or that relied on the pages being a flow of siblings, no longer
|
|
42
|
+
applies — the sheet places its own pages, and does it with inline styles a
|
|
43
|
+
stylesheet rule cannot override. `--qv-sheet-shadow` is unchanged.
|
|
44
|
+
|
|
45
|
+
One cost, stated: a page outside the reach has no `role="img"` node, so
|
|
46
|
+
assistive technology sees the few images of the reach renumbering as the
|
|
47
|
+
reader scrolls rather than a document of named pages.
|
|
48
|
+
|
|
49
|
+
### Patch Changes
|
|
50
|
+
|
|
51
|
+
- **Every published README says where the documentation is.** Each package now
|
|
52
|
+
carries a Documentation section pointing at the reference, at the report schema
|
|
53
|
+
that normatively specifies what a report may declare, and at the package's own
|
|
54
|
+
API. The paragraphs that used to end on an unstated contract — the event
|
|
55
|
+
stream's field semantics, the style vocabulary, page columns, the Content
|
|
56
|
+
Security Policy a fragment with images needs, the formula mangling, and each
|
|
57
|
+
target's own contract — link the page that states it. Every link is an absolute
|
|
58
|
+
URL, so it resolves from the npm package page as readily as from an installed
|
|
59
|
+
copy.
|
|
60
|
+
- Updated dependencies
|
|
61
|
+
- @quario/layout@0.5.0
|
|
62
|
+
- @quario/landing@0.2.0
|
|
63
|
+
- quario@0.8.0
|
|
64
|
+
|
|
65
|
+
## 0.7.0
|
|
66
|
+
|
|
67
|
+
### Minor Changes
|
|
13
68
|
|
|
14
69
|
- **`fonts` is validated as a host property.** It was the one the viewer never
|
|
15
70
|
checked, so a malformed record reached the target inside the render and came
|
|
@@ -18,9 +73,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
18
73
|
on the target's `options.fonts`. A face that will not parse, or a missing
|
|
19
74
|
parser, is still found while the report is measured and remains a render
|
|
20
75
|
failure there.
|
|
21
|
-
|
|
22
|
-
### Changed
|
|
23
|
-
|
|
24
76
|
- **A rejected host property reports as `host-option`.** `ViewerErrorKind`
|
|
25
77
|
gains a fourth member, so a host switching exhaustively over the kind needs
|
|
26
78
|
a case for it. A `page`, `zoom`, `filename`, `colorScheme` or `fonts` the
|
|
@@ -33,9 +85,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
33
85
|
are refused where the render begins rather than when the property is written,
|
|
34
86
|
so nothing yet tells them apart from a render that failed.
|
|
35
87
|
|
|
36
|
-
##
|
|
88
|
+
## 0.6.0
|
|
37
89
|
|
|
38
|
-
###
|
|
90
|
+
### Minor Changes
|
|
39
91
|
|
|
40
92
|
- **Numbers presented through `format` now show a fixed two fraction digits**,
|
|
41
93
|
the same as every other target: `1,000.00` where the preview showed `1,000`,
|
|
@@ -43,9 +95,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
43
95
|
a universal two. The digits come from the engine, so a page on screen and
|
|
44
96
|
the PDF of it agree. A formatted cell is also up to three characters wider,
|
|
45
97
|
so a line that just fitted can wrap and move a page break.
|
|
46
|
-
|
|
47
|
-
### Fixed
|
|
48
|
-
|
|
49
98
|
- **A page the browser cannot fully paint no longer fails the render.** An
|
|
50
99
|
image whose pixel data is corrupt past the size in its header left that page
|
|
51
100
|
blank _and_ rejected `renderComplete`, so a host awaiting it saw an unhandled
|
|
@@ -55,29 +104,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
55
104
|
fires, and no `error` event is raised. It never rejects for this reason
|
|
56
105
|
again: a page that cannot be drawn is blank, not a failed render.
|
|
57
106
|
|
|
58
|
-
##
|
|
107
|
+
## 0.5.0
|
|
59
108
|
|
|
60
|
-
###
|
|
109
|
+
### Minor Changes
|
|
61
110
|
|
|
62
111
|
- **Previewing with `fonts` now needs `fontkit`, not `@pdf-lib/fontkit`.**
|
|
63
112
|
Install `fontkit` instead. The viewer measures host TrueType faces through
|
|
64
113
|
`@quario/layout`, whose optional parser this is; the old package crashed on
|
|
65
114
|
any OpenType face needing a shaping state machine, so those faces now
|
|
66
115
|
preview where they used to throw.
|
|
67
|
-
|
|
68
116
|
- **The bar's controls are drawn icons.** The zoom trigger, the menu's check
|
|
69
117
|
and the error panel's dismiss are drawn from Lucide's set rather than by
|
|
70
118
|
hand, at one weight and one grid, and the dismiss no longer relies on the
|
|
71
119
|
host's font to draw a multiplication sign. They paint in `currentColor`, so
|
|
72
120
|
`--qv-icon` and `--qv-icon-active` still recolour them.
|
|
73
|
-
|
|
74
121
|
- **An export button names its format in words.** Each one is a download icon
|
|
75
122
|
with `PDF`, `XLSX` or `CSV` beside it, in place of the sheet drawing that
|
|
76
123
|
lettered the format inside itself. The buttons are wider; the accessible
|
|
77
124
|
name is unchanged.
|
|
78
|
-
|
|
79
|
-
### Fixed
|
|
80
|
-
|
|
81
125
|
- **A zoom no longer leaves a page part-drawn.** Changing the zoom while a
|
|
82
126
|
page was still painting could leave that page carrying content at the old
|
|
83
127
|
scale on a canvas sized for the new one — the rest of it blank — and nothing
|
|
@@ -85,27 +129,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
85
129
|
whose paint is still in flight is now replaced rather than reused, so the
|
|
86
130
|
superseded paint has nowhere to land.
|
|
87
131
|
|
|
88
|
-
##
|
|
132
|
+
## 0.4.0
|
|
89
133
|
|
|
90
|
-
###
|
|
134
|
+
### Minor Changes
|
|
91
135
|
|
|
92
136
|
- **`fonts`.** The font mapping a host passes to `pdf({ fonts })`, so the
|
|
93
137
|
preview measures and draws in the same TrueType faces the document embeds.
|
|
94
|
-
|
|
95
|
-
### Changed
|
|
96
|
-
|
|
97
138
|
- **`renderComplete` and `rendered` settle once the pages on screen are
|
|
98
139
|
painted.** Both already answered for the newest render reaching the sheet;
|
|
99
140
|
what that means is now stated: the pages the reader can see carry their
|
|
100
141
|
pixels, and the pages further down the report do not hold the promise up.
|
|
101
142
|
A screenshot or a pixel-reading test taken at that moment sees what the
|
|
102
143
|
reader sees.
|
|
103
|
-
|
|
104
144
|
- **A split slot's box now fills the split's height.** A slot's background and
|
|
105
145
|
border stopped at that slot's own content and now take the split's height,
|
|
106
146
|
the way a table cell's take their row's. See the `@quario/layout` changelog
|
|
107
147
|
for the rule and for what it costs a report that relied on the short box.
|
|
108
|
-
|
|
109
148
|
- **The sheet has pages, and they break where the PDF's do.** The viewer
|
|
110
149
|
depends on `@quario/layout` and paints the same display list the pdf target
|
|
111
150
|
writes, one canvas per page, in the faces the document itself uses.
|
|
@@ -115,9 +154,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
115
154
|
`page` change re-lays the report out rather than resizing a sheet. The
|
|
116
155
|
unlicensed marking is painted per page from the list; the `--qv-mark`
|
|
117
156
|
token is gone with the DOM stamps.
|
|
118
|
-
|
|
119
|
-
### Fixed
|
|
120
|
-
|
|
121
157
|
- **A long report no longer asks for a backing store per page.** The viewer
|
|
122
158
|
paints the pages near the viewport — the ones on screen and one screenful
|
|
123
159
|
either side — and keeps every other page sized but blank. A report of a
|
|
@@ -128,23 +164,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
128
164
|
the first frame, and a zoom step now repaints a handful of pages instead of
|
|
129
165
|
walking the document.
|
|
130
166
|
|
|
131
|
-
##
|
|
167
|
+
## 0.3.0
|
|
132
168
|
|
|
133
|
-
###
|
|
169
|
+
### Minor Changes
|
|
134
170
|
|
|
135
171
|
- **Report and group containers are flex columns.** Adjacent authored
|
|
136
172
|
`spaceBefore` / `spaceAfter` margins add rather than collapse, matching
|
|
137
173
|
the html reference stylesheet.
|
|
138
|
-
|
|
139
|
-
### Fixed
|
|
140
|
-
|
|
141
174
|
- **The adopted sheet occupies a line and preserves authored newlines.**
|
|
142
175
|
Same `min-height: 1lh` and `white-space: pre-line` the html reference
|
|
143
176
|
stylesheet now carries on `.q-item`, and `pre-line` on table cells.
|
|
144
177
|
|
|
145
|
-
##
|
|
178
|
+
## 0.2.0
|
|
146
179
|
|
|
147
|
-
###
|
|
180
|
+
### Minor Changes
|
|
148
181
|
|
|
149
182
|
- **The sheet is now page-shaped, so short reports reserve a full page.**
|
|
150
183
|
`page` sized the sheet's width and padding and ignored its height, so a
|
|
@@ -163,9 +196,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
163
196
|
- **The adopted sheet's baseline face moved onto `.q-report`.** It was on
|
|
164
197
|
`.q-item` and `.q-table`, where a direct rule would have beaten a report's
|
|
165
198
|
own declared family. Same look; the fragment's new root is what carries it.
|
|
166
|
-
|
|
167
|
-
### Fixed
|
|
168
|
-
|
|
169
199
|
- **The sheet no longer draws report text in the platform's own face.** It
|
|
170
200
|
hard-coded `system-ui`, which is SF Pro on macOS, Segoe UI on Windows and
|
|
171
201
|
Roboto on Android — so the same report showed a different typeface to every
|
|
@@ -174,9 +204,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
174
204
|
resolves to Helvetica. The viewer's own chrome keeps `system-ui`, which is
|
|
175
205
|
what a toolbar should do.
|
|
176
206
|
|
|
177
|
-
##
|
|
207
|
+
## 0.1.0
|
|
178
208
|
|
|
179
|
-
###
|
|
209
|
+
### Minor Changes
|
|
180
210
|
|
|
181
211
|
- **An embeddable `<quario-viewer>`.** Assign a compiled report, the targets
|
|
182
212
|
to render with, and data; it shows the HTML report on a continuous sheet,
|
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
**The embeddable report viewer for [quario](https://www.npmjs.com/package/quario), as a custom
|
|
4
4
|
element.** Write `<quario-viewer>`, assign it a compiled report, the export targets you want, and
|
|
5
|
-
data
|
|
5
|
+
data. It lays the report out on pages — the same pages the PDF target writes — paints them on a
|
|
6
6
|
white sheet in its shadow root, and offers export downloads for the targets you passed.
|
|
7
7
|
|
|
8
8
|
The viewer is not a render target and compiles nothing. The sheet is `@quario/layout`'s display
|
|
9
|
-
list, painted page by page
|
|
10
|
-
to each (`"pdf"`, `"xlsx"`, `"csv"`). The bar always carries the zoom control
|
|
11
|
-
target it carries nothing else.
|
|
9
|
+
list, painted page by page. You pass the export targets you want, and the viewer wires a button
|
|
10
|
+
to each (`"pdf"`, `"xlsx"`, `"csv"`, `"docx"`). The bar always carries the zoom control. With no
|
|
11
|
+
exportable target it carries nothing else.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -16,10 +16,10 @@ target it carries nothing else.
|
|
|
16
16
|
npm install quario @quario/viewer
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The engine is a peer
|
|
20
|
-
to export with (`@quario/pdf`, `@quario/xlsx`, `@quario/csv`) and pass them in. Its
|
|
21
|
-
dependencies are [Lit](https://lit.dev) (`lit` + `@lit/task`) and `@quario/layout`, the
|
|
22
|
-
layout it paints, plain ESM like everything else here. ESM-only, and
|
|
19
|
+
The engine is a peer. The viewer itself depends on no target package. Install the targets you want
|
|
20
|
+
to export with (`@quario/pdf`, `@quario/xlsx`, `@quario/csv`, `@quario/docx`) and pass them in. Its
|
|
21
|
+
own runtime dependencies are [Lit](https://lit.dev) (`lit` + `@lit/task`) and `@quario/layout`, the
|
|
22
|
+
paged layout it paints, plain ESM like everything else here. ESM-only, and
|
|
23
23
|
browser-only by nature: the element needs a DOM. CSP-safe like the rest of quario. No
|
|
24
24
|
string-to-code paths, chrome styled through constructed stylesheets, so your `style-src` never
|
|
25
25
|
sees a style tag.
|
|
@@ -28,6 +28,7 @@ sees a style tag.
|
|
|
28
28
|
|
|
29
29
|
```js
|
|
30
30
|
import { csv } from "@quario/csv";
|
|
31
|
+
import { docx } from "@quario/docx";
|
|
31
32
|
import { pdf } from "@quario/pdf";
|
|
32
33
|
import { xlsx } from "@quario/xlsx";
|
|
33
34
|
import { quario } from "quario";
|
|
@@ -35,14 +36,14 @@ import "@quario/viewer/register";
|
|
|
35
36
|
|
|
36
37
|
const view = document.querySelector("quario-viewer");
|
|
37
38
|
view.report = quario().report(schema, funcs);
|
|
38
|
-
view.targets = [pdf(), xlsx(), csv()];
|
|
39
|
+
view.targets = [pdf(), xlsx(), csv(), docx()];
|
|
39
40
|
view.data = data;
|
|
40
41
|
view.filename = "sales";
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
with `<quario-viewer></quario-viewer>` in your markup, sized by your own CSS. The element is
|
|
44
|
-
`display: block`, and its chrome
|
|
45
|
-
the
|
|
45
|
+
`display: block`, and its chrome fits a width of **320px**, a phone-width embed and the narrowest
|
|
46
|
+
the bar supports. Narrower than that and the export buttons push the zoom
|
|
46
47
|
control off the left of the bar, quietly: the bar does not wrap or scroll. Importing
|
|
47
48
|
`@quario/viewer` defines nothing: the main entry exports the `QuarioViewer` class and is
|
|
48
49
|
side-effect-free, and `@quario/viewer/register` performs the one-line
|
|
@@ -67,14 +68,14 @@ package to install. See [Using with React, Vue and Svelte](#using-with-react-vue
|
|
|
67
68
|
| `colorScheme` | `"light"`, `"dark"`, or `"auto"` | `"light"` |
|
|
68
69
|
|
|
69
70
|
Properties, not attributes: `report`, `targets`, `data`, `page` and `fonts` are values no attribute
|
|
70
|
-
could carry. `targets` mirrors `report.render(target, data)` for the exports:
|
|
71
|
-
targets become export buttons, in the order given, and the
|
|
72
|
-
instance (and with it the license and the registry) stays
|
|
73
|
-
report, never a schema.
|
|
71
|
+
could carry. `targets` mirrors `report.render(target, data)` for the exports:
|
|
72
|
+
`"pdf"`/`"xlsx"`/`"csv"`/`"docx"` targets become export buttons, in the order given, and the
|
|
73
|
+
sheet needs none of them. The quario instance (and with it the license and the registry) stays
|
|
74
|
+
yours: the element takes the compiled report, never a schema.
|
|
74
75
|
|
|
75
76
|
Assigning `data` (or any of the others) re-renders. Rapid successive writes render only the newest
|
|
76
77
|
state: a slow render can never overwrite a newer one, and a superseded render fires no event.
|
|
77
|
-
|
|
78
|
+
The viewer compares assignments by identity, so to re-render from the same object, assign a fresh one
|
|
78
79
|
(`view.data = { ...data }`).
|
|
79
80
|
|
|
80
81
|
`page` and `fonts` are the pdf target's own options: the viewer lays the report out on them, so
|
|
@@ -90,32 +91,32 @@ view.addEventListener("error", ({ detail: { error, kind } }) => {});
|
|
|
90
91
|
await view.renderComplete; // true when the newest render landed on the sheet
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
`rendered` fires each time a render lands on the sheet
|
|
94
|
+
`rendered` fires each time a render lands on the sheet. `error` fires for every failure you should
|
|
94
95
|
know about, with `detail.kind` naming which: `"mount-render"` until a render has ever landed,
|
|
95
96
|
`"update-render"` after, `"export"` for a download that could not be produced. Both events are
|
|
96
|
-
non-bubbling, like `<img>`'s. Listen on the element. (Because the event
|
|
97
|
-
inline `onerror` attribute on the element would fire too
|
|
97
|
+
non-bubbling, like `<img>`'s. Listen on the element. (Because the event carries the name `error`, an
|
|
98
|
+
inline `onerror` attribute on the element would fire too. `window.onerror` never sees it.)
|
|
98
99
|
|
|
99
100
|
`renderComplete` awaits the newest render settling: `true` when it landed on the sheet with the pages
|
|
100
101
|
on screen finished trying to paint, `false` when it failed or there was nothing to render. It never
|
|
101
|
-
rejects
|
|
102
|
-
outcome here, it answers for the newest render only. A superseded render's failure
|
|
102
|
+
rejects. Failures arrive on the `error` event. `rendered` fires at that same moment. Like every
|
|
103
|
+
outcome here, it answers for the newest render only. A superseded render's failure reaches no
|
|
103
104
|
one.
|
|
104
105
|
|
|
105
106
|
An image the browser cannot decode — pixel data corrupt past the size in its header, which is all
|
|
106
107
|
the engine reads — is drawn as nothing, and the page is drawn around it. That is not a render
|
|
107
|
-
failure: `renderComplete` answers `true`, `rendered` fires, and no `error` event
|
|
108
|
-
`fonts` is a different story
|
|
109
|
-
are a render error with a panel.
|
|
108
|
+
failure: `renderComplete` answers `true`, `rendered` fires, and no `error` event follows. A face in
|
|
109
|
+
`fonts` is a different story. The layout parses it while it measures the report, so bytes that will
|
|
110
|
+
not parse are a render error with a panel.
|
|
110
111
|
|
|
111
112
|
Your own mistakes surface on the same channel: a report that is not compiled, or a malformed
|
|
112
113
|
option property, becomes a `TypeError` naming the property, on the `error` event and the
|
|
113
|
-
[error panel](#errors).
|
|
114
|
+
[error panel](#errors). The viewer needs no particular target — the sheet is the layout's own.
|
|
114
115
|
|
|
115
116
|
## Lifecycle
|
|
116
117
|
|
|
117
118
|
There is no `destroy()`. Removing the element from the DOM abandons in-flight work and releases
|
|
118
|
-
its observers
|
|
119
|
+
its observers. An export that settles after removal downloads nothing. The properties persist, and
|
|
119
120
|
re-inserting the element re-renders from them, so reparenting is safe, and discarding a viewer is
|
|
120
121
|
just discarding the element. Two viewers, or a viewer beside your own components, coexist: each
|
|
121
122
|
element owns its own shadow root.
|
|
@@ -123,7 +124,7 @@ element owns its own shadow root.
|
|
|
123
124
|
## Using with React, Vue and Svelte
|
|
124
125
|
|
|
125
126
|
No wrapper packages: each framework sets properties on custom elements directly, so the samples
|
|
126
|
-
below are the whole integration.
|
|
127
|
+
below are the whole integration. Each of the three ran before landing here. Import
|
|
127
128
|
`@quario/viewer/register` once, anywhere before the component mounts, and size the element with
|
|
128
129
|
your own CSS.
|
|
129
130
|
|
|
@@ -150,7 +151,7 @@ function Report({ report, targets, data }) {
|
|
|
150
151
|
}
|
|
151
152
|
```
|
|
152
153
|
|
|
153
|
-
Re-rendering with a new `data` prop re-renders the report
|
|
154
|
+
Re-rendering with a new `data` prop re-renders the report. The newest write wins, as always.
|
|
154
155
|
(React 18 and earlier stringify unknown props to attributes. There, hold a `ref` and assign the
|
|
155
156
|
properties and listeners in an effect.)
|
|
156
157
|
|
|
@@ -213,46 +214,47 @@ listeners:
|
|
|
213
214
|
|
|
214
215
|
The bar's magnifier opens the zoom menu: **Fit page** on its own, then 25%, 50%, 75%, 100%, 150%
|
|
215
216
|
and 200%. A check marks the current mode — Fit page whenever the viewer is fitting, whatever
|
|
216
|
-
percentage that came out at — and the percentage on screen
|
|
217
|
+
percentage that came out at — and the trigger carries the percentage on screen in its name
|
|
217
218
|
("Zoom, 62%"), so nothing in the bar changes width as it moves. The `zoom` property picks the mode
|
|
218
219
|
the viewer opens in: a percentage between 25 and 200 — continuous, not one of the menu's stops — or
|
|
219
220
|
`"fit"`, the default. A percentage the menu does not offer leaves every row unchecked.
|
|
220
221
|
|
|
221
222
|
Fit sizes one page to the viewer's width and **only ever shrinks**: given room to spare it stops at
|
|
222
|
-
100%, so the report
|
|
223
|
+
100%, so the report keeps its true point size rather than growing past it. It has no floor, so a
|
|
223
224
|
narrow pane fits at whatever percentage that takes — below 25% the menu has no row to return to it,
|
|
224
225
|
and **Fit page** is the only way back. A fitted viewer follows its own box, so a collapsing panel
|
|
225
226
|
or a resized window re-fits on its own.
|
|
226
227
|
|
|
227
|
-
The preview **scales
|
|
228
|
+
The preview **scales. It never reflows.** Zooming repaints the pages larger or smaller, like a
|
|
228
229
|
photograph that stays sharp. Line breaks, column widths and point sizes stay what they are at
|
|
229
230
|
100%, because the layout never changes under a zoom.
|
|
230
231
|
|
|
231
|
-
|
|
232
|
+
The viewer paints only the pages near the viewport — the ones on screen and one screenful either side.
|
|
232
233
|
Every page keeps its size, so the scrollbar and the scroll extent are the whole report from the
|
|
233
|
-
start
|
|
234
|
+
start. A page further off is blank paper until you scroll to it, which is why a zoom step costs the
|
|
234
235
|
same on a thousand-page report as on a five-page one.
|
|
235
236
|
|
|
236
237
|
## What the preview is
|
|
237
238
|
|
|
238
239
|
The pages on screen are the pages the PDF export writes: both consume one layout, `@quario/layout`'s
|
|
239
|
-
display list, laid out on your `page` and `fonts`. A report shorter than a page is one page
|
|
240
|
-
longer one is as many as the layout breaks it into, stacked down the sheet. Sizing a
|
|
241
|
-
viewer
|
|
242
|
-
enough where the box has to stay small.
|
|
243
|
-
|
|
244
|
-
Each page is a canvas, and text on it is drawn in the face the document will use.
|
|
245
|
-
family you pass as `fonts`
|
|
240
|
+
display list, laid out on your `page` and `fonts`. A report shorter than a page is one page. A
|
|
241
|
+
longer one is as many pages as the layout breaks it into, stacked down the sheet. Sizing a
|
|
242
|
+
container to a viewer that draws a short report therefore reserves a full page. Set `zoom` to a
|
|
243
|
+
percentage small enough where the box has to stay small.
|
|
244
|
+
|
|
245
|
+
Each page is a canvas, and text on it is drawn in the face the document will use. The viewer
|
|
246
|
+
registers a TrueType family you pass as `fonts` from your own bytes and draws it as the browser
|
|
247
|
+
shapes it —
|
|
246
248
|
the same shaping the PDF gets from the same file, so ligatures, joined scripts and accents look
|
|
247
249
|
here the way they will on paper. Text in the built-in families is drawn character by character at
|
|
248
250
|
the advances the layout measured, because the font a browser has for Helvetica, Times or Courier
|
|
249
251
|
only stands in for the one the PDF writes, and the correction is what makes a line fill the same
|
|
250
252
|
width and break in the same place the document does. Inside one run of a family you supply, a
|
|
251
|
-
browser may kern by a fraction more than the document
|
|
252
|
-
layout's, and identical.
|
|
253
|
+
browser may kern by a fraction more than the document. Where lines break and pages end is the
|
|
254
|
+
layout's, and identical. The viewer decodes images from their bytes — no `data:` URIs, so a host page's
|
|
253
255
|
Content Security Policy needs no `img-src` grant for them.
|
|
254
256
|
|
|
255
|
-
Rendering an unlicensed evaluation, the
|
|
257
|
+
Rendering an unlicensed evaluation, the viewer paints the marking across every page, the way the PDF
|
|
256
258
|
export marks every page: it rides on the layout, so what you see is what the document carries. A
|
|
257
259
|
licensed render carries none.
|
|
258
260
|
|
|
@@ -262,8 +264,8 @@ reads `aria-busy="true"`. It reports that the viewer is working, not how far alo
|
|
|
262
264
|
engine streams events and cannot know how many are still coming.
|
|
263
265
|
|
|
264
266
|
There is no Print button, because the sheet is the wrong thing to print. Your page's stylesheets do
|
|
265
|
-
not cross into the shadow root, so `@media print` rules never reach the report
|
|
266
|
-
|
|
267
|
+
not cross into the shadow root, so `@media print` rules never reach the report. The viewer also
|
|
268
|
+
paints the pages at whatever zoom the reader happened to leave them, so printing the page puts the viewer's
|
|
267
269
|
chrome on paper at that zoom. What a printer wants is the pdf target's
|
|
268
270
|
document, the same bytes the PDF export hands over. A host that wants its own Print button owns
|
|
269
271
|
two lines:
|
|
@@ -280,19 +282,23 @@ the export is, watermark and all.
|
|
|
280
282
|
|
|
281
283
|
## Errors
|
|
282
284
|
|
|
283
|
-
When a render or an export fails, the viewer says so on the **error panel
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
When a render or an export fails, the viewer says so on the **error panel**. The panel is a strip
|
|
286
|
+
across the top of the sheet, carrying the error's own message under one of three short labels:
|
|
287
|
+
|
|
288
|
+
- "Could not render the report"
|
|
289
|
+
- "Could not update the report — showing the previous version"
|
|
290
|
+
- "Could not export PDF" It replaces rather than stacks, its own
|
|
291
|
+
button dismisses it, and the next render that lands on the sheet clears it. A successful
|
|
292
|
+
export leaves it up, because the panel describes what you are looking at and a download says
|
|
293
|
+
nothing about that.
|
|
288
294
|
|
|
289
|
-
Every failure the panel draws, and every export failure, also fires the `error` event
|
|
290
|
-
|
|
295
|
+
Every failure the panel draws, and every export failure, also fires the `error` event. The viewer rethrows
|
|
296
|
+
nothing to the platform behind it. Failures after the element leaves the DOM go unreported, and a
|
|
291
297
|
render that a newer one has already superseded reports to no one at all.
|
|
292
298
|
|
|
293
299
|
The message carries quario's **located error**, the band/item path and the offending source, as in
|
|
294
|
-
`detail[0] [{{ @.amount.toFixed(2) }}]: ...`.
|
|
295
|
-
trusted configuration
|
|
300
|
+
`detail[0] [{{ @.amount.toFixed(2) }}]: ...`. Drawing it is safe because report definitions are
|
|
301
|
+
trusted configuration. Report _data_ never is, and none of it appears in the path. What can carry
|
|
296
302
|
data is the message itself, if your own registry functions interpolate a row into what they throw.
|
|
297
303
|
That is your call, and the panel puts it on screen as text, never as markup. Compile errors are not
|
|
298
304
|
part of this: `q.report(schema)` raises those before the viewer is ever handed a report.
|
|
@@ -300,7 +306,7 @@ part of this: `q.report(schema)` raises those before the viewer is ever handed a
|
|
|
300
306
|
## Color scheme
|
|
301
307
|
|
|
302
308
|
`colorScheme` paints the **chrome** — backdrop, bar, controls, progress strip, error
|
|
303
|
-
panel — and never the sheet. `"light"` (the default) and `"dark"` pin
|
|
309
|
+
panel — and never the sheet. `"light"` (the default) and `"dark"` pin. `"auto"` follows
|
|
304
310
|
the OS via CSS `color-scheme`. The pages stay white, marking included.
|
|
305
311
|
|
|
306
312
|
Chrome styles live on `--qv-*` custom properties under stable `qv-*` class names. A
|
|
@@ -314,12 +320,19 @@ reference seam, not a frozen vocabulary: backdrop and bar (`--qv-backdrop`, `--q
|
|
|
314
320
|
The report itself is paint, not markup: there is no report stylesheet to restyle, because what
|
|
315
321
|
you see is the layout the PDF target writes.
|
|
316
322
|
|
|
323
|
+
## Documentation
|
|
324
|
+
|
|
325
|
+
[The quario documentation](https://getquario.com/docs/) is the reference.
|
|
326
|
+
The [report schema](https://getquario.com/docs/reference/report-schema/) is the normative
|
|
327
|
+
specification of what a report may declare, and
|
|
328
|
+
[`@quario/viewer`](https://getquario.com/docs/reference/viewer/) is this element's own API.
|
|
329
|
+
|
|
317
330
|
## License
|
|
318
331
|
|
|
319
|
-
Commercial software with readable source.
|
|
320
|
-
licenses at [getquario.com](https://getquario.com). See the bundled LICENSE.
|
|
332
|
+
Commercial software with readable source. Evaluation is free, unlimited, and watermarked.
|
|
333
|
+
Per-developer licenses at [getquario.com](https://getquario.com). See the bundled LICENSE.
|
|
321
334
|
|
|
322
|
-
Pass your license key once, on the instance
|
|
335
|
+
Pass your license key once, on the instance. quario verifies it offline:
|
|
323
336
|
|
|
324
337
|
```js
|
|
325
338
|
const q = quario({ license: "quario_..." });
|
package/lib/icons.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* default, 14px in the gutter and the menu's tick slot — because `x` appears
|
|
5
5
|
* in two contexts at once (ADR 0044).
|
|
6
6
|
*
|
|
7
|
-
* Generated by `scripts/icons.mjs` from lucide 1.
|
|
7
|
+
* Generated by `scripts/icons.mjs` from lucide 1.42.0. Do not edit:
|
|
8
8
|
* `npm run icons` rewrites it, and `npm run check` regenerates it into a
|
|
9
9
|
* temp directory and compares.
|
|
10
10
|
*
|
package/lib/index.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export class QuarioViewer extends LitElement {
|
|
|
67
67
|
report: ViewableReport | undefined;
|
|
68
68
|
/**
|
|
69
69
|
* The export targets, as passed to `report.render`: ones named
|
|
70
|
-
* `"pdf"`/`"xlsx"`/`"csv"` become export buttons, in array order. The
|
|
70
|
+
* `"pdf"`/`"xlsx"`/`"csv"`/`"docx"` become export buttons, in array order. The
|
|
71
71
|
* sheet needs none of them — it is the layout's own. An empty array is
|
|
72
72
|
* a viewer with nothing to export.
|
|
73
73
|
*/
|
package/lib/stage.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The stage: the surface the pages are painted on. It owns the scroll
|
|
3
3
|
* container the reader looks through, the wrapper that carries the gutter,
|
|
4
|
-
* and the sheet —
|
|
5
|
-
* `@quario/layout`'s `paint` — their elements, their
|
|
6
|
-
* relating them.
|
|
4
|
+
* and the sheet — the layout list's whole extent, with a canvas for each page
|
|
5
|
+
* of the reach painted by `@quario/layout`'s `paint` — their elements, their
|
|
6
|
+
* CSS, and every number relating them.
|
|
7
7
|
*
|
|
8
8
|
* Zoom is a repaint, not a transform: each page canvas is sized to the
|
|
9
9
|
* percentage in CSS pixels and painted at that scale times the device pixel
|
|
@@ -11,15 +11,17 @@
|
|
|
11
11
|
* reader saw. The list never changes under a zoom — the preview scales and
|
|
12
12
|
* never reflows (zoom.js says why).
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* [reach](../../../CONTEXT.md#reach) carries pixels — the pages
|
|
16
|
-
* one viewport height either side. The extent the reader scrolls
|
|
17
|
-
* whole and synchronous whatever is
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* the
|
|
14
|
+
* The sheet carries the whole list's extent, but only the
|
|
15
|
+
* [reach](../../../CONTEXT.md#reach) carries elements and pixels — the pages
|
|
16
|
+
* on screen and one viewport height either side. The extent the reader scrolls
|
|
17
|
+
* through is whole and synchronous whatever is standing, because the sheet is
|
|
18
|
+
* given its height and width inline and paints the page silhouettes itself; a
|
|
19
|
+
* page leaving the reach is removed, which takes its backing store with it
|
|
20
|
+
* (ADR 0065). A scroll or a resize runs the same arithmetic over the list's own
|
|
21
|
+
* geometry, coalesced to one pass a frame. ADR 0043 says what the reach is
|
|
22
|
+
* worth: painting the whole sheet asked a thousand-page report for gigabytes of
|
|
23
|
+
* backing store, and past what the browser would grant the pages simply came up
|
|
24
|
+
* blank.
|
|
23
25
|
*
|
|
24
26
|
* **A page the browser will not paint is blank, not a failure.** `start`
|
|
25
27
|
* swallows and nothing here rejects: the report laid out and the list reached
|
|
@@ -39,13 +41,14 @@
|
|
|
39
41
|
* *retired* rather than painted over or emptied: it comes off the sheet, its
|
|
40
42
|
* pixels go back, and a fresh one stands in its place, so the superseded
|
|
41
43
|
* paint draws into an element nobody is looking at. A canvas whose paint has
|
|
42
|
-
* settled has nothing that could land late and is re-sized
|
|
43
|
-
*
|
|
44
|
+
* settled has nothing that could land late and is re-sized in place as before;
|
|
45
|
+
* a page leaving the reach is removed either way, which takes its pixels with
|
|
46
|
+
* it whether that paint has landed or not. Without this a zoom landing mid-paint could leave a page
|
|
44
47
|
* carrying old-scale content on a new-scale canvas, with the memo below
|
|
45
48
|
* calling it painted so that nothing repainted it again (ADR 0046).
|
|
46
49
|
*
|
|
47
|
-
* Retirement is lazy, and only the paint is guarded. A scale change sizes
|
|
48
|
-
*
|
|
50
|
+
* Retirement is lazy, and only the paint is guarded. A scale change sizes the
|
|
51
|
+
* reach's CSS boxes at once but retires a page only when the repaint loop
|
|
49
52
|
* reaches it, so a page further down the reach shows its old pixels stretched
|
|
50
53
|
* into the new box until its turn comes — the ordinary look of a zoom in
|
|
51
54
|
* progress, not the artefact above. The cost is that a retired page is a
|
|
@@ -70,12 +73,14 @@
|
|
|
70
73
|
*/
|
|
71
74
|
|
|
72
75
|
import { css } from "lit";
|
|
73
|
-
import {
|
|
76
|
+
import { GUTTER, sheet } from "@quario/landing";
|
|
74
77
|
import { PX_PER_POINT } from "@quario/layout";
|
|
75
78
|
|
|
76
|
-
// `GUTTER`
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
+
// `GUTTER` comes from `@quario/landing`, which walks the reach over it: the CSS
|
|
80
|
+
// below and the sheet's own arithmetic have to agree about where a page sits,
|
|
81
|
+
// and two copies of that is a reach that paints the wrong pages. The gap
|
|
82
|
+
// between pages is that package's too, and is not named here at all — the sheet
|
|
83
|
+
// positions its own pages now, so this file has nothing to space.
|
|
79
84
|
|
|
80
85
|
export let SURFACE = css`
|
|
81
86
|
/* Where scrollbars take width, the gutter is held whether one is showing or
|
|
@@ -101,16 +106,17 @@ export let SURFACE = css`
|
|
|
101
106
|
margin: ${GUTTER}px auto;
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
/* The sheet is the
|
|
105
|
-
.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
/* The sheet is the whole document's extent, and the pages of the reach are
|
|
110
|
+
positioned inside it. It has no rule here because it has no look: its
|
|
111
|
+
position, its height, its width and its page silhouettes are all written
|
|
112
|
+
inline by \`@quario/landing\` from the list's own geometry, which is the one
|
|
113
|
+
place any of them is decided (ADR 0065). A page outside the reach has no
|
|
114
|
+
element, so what shows there is the sheet's own paint.
|
|
110
115
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
and being painted
|
|
116
|
+
One page of the reach, at the size and offset the sheet gives it. The
|
|
117
|
+
painter fills it white; the background here is what it shows between being
|
|
118
|
+
placed and being painted, and the shadow is what lifts it off the sheet —
|
|
119
|
+
the two this file does decide. */
|
|
114
120
|
.qv-page {
|
|
115
121
|
display: block;
|
|
116
122
|
background: #fff;
|
|
@@ -191,14 +197,17 @@ export let stage = () => {
|
|
|
191
197
|
* offset; horizontally the wrapper is centred by auto margins while it
|
|
192
198
|
* fits — which is exactly when `scrollLeft` is 0 anyway — and its margins
|
|
193
199
|
* are 0 once it overflows, so the plain ratio holds wherever it can be
|
|
194
|
-
* seen. The browser clamps whatever it cannot honour.
|
|
195
|
-
* no view to hold, which is what mounting at an authored zoom
|
|
200
|
+
* seen. The browser clamps whatever it cannot honour. A sheet with no list
|
|
201
|
+
* on it has no view to hold, which is what mounting at an authored zoom
|
|
202
|
+
* takes. Asked of the list rather than of the sheet's children, which
|
|
203
|
+
* since ADR 0065 are the reach's and can be none of them while a list is
|
|
204
|
+
* standing — and it saves a DOM read besides.
|
|
196
205
|
*
|
|
197
206
|
* Only the reach is repainted, and only where the scale actually changed,
|
|
198
207
|
* so a zoom step costs a handful of pages however long the report is.
|
|
199
208
|
*/
|
|
200
209
|
scale: (percent) => {
|
|
201
|
-
let held =
|
|
210
|
+
let held = paged.count() > 0 && {
|
|
202
211
|
top: scroll.scrollTop,
|
|
203
212
|
left: scroll.scrollLeft,
|
|
204
213
|
height: scroll.clientHeight,
|
|
@@ -228,12 +237,12 @@ export let stage = () => {
|
|
|
228
237
|
|
|
229
238
|
/**
|
|
230
239
|
* Put a laid-out report on the sheet, keeping the reader where they
|
|
231
|
-
* were:
|
|
232
|
-
* scale. The order is the point of the method: the
|
|
233
|
-
*
|
|
234
|
-
* back against the extent the sheet will have rather than
|
|
235
|
-
* — and the reach is read from those offsets, so it is
|
|
236
|
-
* are in. What the
|
|
240
|
+
* were: the extent whole at once, with the reach's pages painted at the
|
|
241
|
+
* applied scale. The order is the point of the method: the sheet takes its
|
|
242
|
+
* height from the list before anything else, so the browser clamps the
|
|
243
|
+
* offsets going back against the extent the sheet will have rather than
|
|
244
|
+
* the one it had — and the reach is read from those offsets, so it is
|
|
245
|
+
* chosen after they are in. What the
|
|
237
246
|
* returned promise settles behind is the reach, which is what the caller's
|
|
238
247
|
* `renderComplete` means by "the pages on screen have finished trying to
|
|
239
248
|
* paint" — and **it never rejects**, because `start` swallows a page the
|
|
@@ -241,8 +250,8 @@ export let stage = () => {
|
|
|
241
250
|
*/
|
|
242
251
|
swap: async (next, faces) => {
|
|
243
252
|
// Reading the offsets flushes layout, so only an actual reswap pays for
|
|
244
|
-
// it:
|
|
245
|
-
let held =
|
|
253
|
+
// it: with no list standing there is nothing scrolled to preserve.
|
|
254
|
+
let held = paged.count() > 0 && {
|
|
246
255
|
top: scroll.scrollTop,
|
|
247
256
|
left: scroll.scrollLeft,
|
|
248
257
|
};
|
package/lib/toolbar.js
CHANGED
|
@@ -22,6 +22,7 @@ export let EXPORTS = {
|
|
|
22
22
|
pdf: "application/pdf",
|
|
23
23
|
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
24
24
|
csv: "text/csv",
|
|
25
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
// The anchor joins the document for the click — detached-anchor downloads
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quario/viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "The embeddable report viewer shell for quario — in the makings, not yet released",
|
|
5
5
|
"homepage": "https://getquario.com",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -44,22 +44,22 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@lit/task": "^1.0.3",
|
|
47
|
-
"@quario/landing": "^0.1
|
|
48
|
-
"@quario/layout": "^0.
|
|
47
|
+
"@quario/landing": "^0.2.1",
|
|
48
|
+
"@quario/layout": "^0.6.0",
|
|
49
49
|
"lit": "^3.3.3"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@arethetypeswrong/cli": "^0.18.3",
|
|
53
|
-
"@cantoo/pdf-lib": "
|
|
53
|
+
"@cantoo/pdf-lib": "~2.9.1",
|
|
54
54
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
55
55
|
"esbuild": "^0.28.2",
|
|
56
56
|
"exceljs": "^4.4.0",
|
|
57
|
-
"quario": "^0.
|
|
57
|
+
"quario": "^0.9.0",
|
|
58
58
|
"size-limit": "^13.0.3",
|
|
59
59
|
"typescript": "^7.0.2"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"quario": "^0.
|
|
62
|
+
"quario": "^0.9.0"
|
|
63
63
|
},
|
|
64
64
|
"size-limit": [
|
|
65
65
|
{
|