@quario/viewer 0.8.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 +18 -0
- package/README.md +64 -58
- package/lib/index.d.ts +1 -1
- package/lib/toolbar.js +1 -0
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @quario/viewer
|
|
2
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
|
+
|
|
3
21
|
## 0.8.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
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
|
|
@@ -323,10 +329,10 @@ specification of what a report may declare, and
|
|
|
323
329
|
|
|
324
330
|
## License
|
|
325
331
|
|
|
326
|
-
Commercial software with readable source.
|
|
327
|
-
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.
|
|
328
334
|
|
|
329
|
-
Pass your license key once, on the instance
|
|
335
|
+
Pass your license key once, on the instance. quario verifies it offline:
|
|
330
336
|
|
|
331
337
|
```js
|
|
332
338
|
const q = quario({ license: "quario_..." });
|
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/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.2.
|
|
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
|
{
|