@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 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; it lays the report out on pages — the same pages the PDF target writes — paints them on a
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; you pass the export targets you want, and the viewer wires a button
10
- to each (`"pdf"`, `"xlsx"`, `"csv"`). The bar always carries the zoom control; with no exportable
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; the viewer itself depends on no target package. Install the targets you want
20
- to export with (`@quario/pdf`, `@quario/xlsx`, `@quario/csv`) and pass them in. Its own runtime
21
- dependencies are [Lit](https://lit.dev) (`lit` + `@lit/task`) and `@quario/layout`, the paged
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 is laid out to fit a width of **320px** a phone-width embed is
45
- the narrowest the bar is designed for. Narrower than that and the export buttons push the zoom
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: `"pdf"`/`"xlsx"`/`"csv"`
71
- targets become export buttons, in the order given, and the sheet needs none of them. The quario
72
- instance (and with it the license and the registry) stays yours: the element takes the compiled
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
- Assignments are compared by identity, so to re-render from the same object, assign a fresh one
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; `error` fires for every failure you should
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 is named `error`, an
97
- inline `onerror` attribute on the element would fire too; `window.onerror` never sees it.)
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; failures arrive on the `error` event. `rendered` fires at that same moment. Like every
102
- outcome here, it answers for the newest render only. A superseded render's failure is reported to no
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 is raised. A face in
108
- `fonts` is a different story: it is parsed while the report is measured, so bytes that will not parse
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). No particular target is required — the sheet is the layout's own.
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; an export that settles after removal downloads nothing. The properties persist, and
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. All three were run before landing here. Import
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; the newest write wins, as always.
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 is what the trigger is named after
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 is shown at its true point size rather than blown up. It has no floor, so a
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; it never reflows.** Zooming repaints the pages larger or smaller, like a
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
- Only the pages near the viewport are painted — the ones on screen and one screenful either side.
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; a page further off is blank paper until you scroll to it, which is why a zoom step costs the
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; a
240
- longer one is as many as the layout breaks it into, stacked down the sheet. Sizing a container to a
241
- viewer showing a short report therefore reserves a full page set `zoom` to a percentage small
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. A TrueType
245
- family you pass as `fonts` is registered from your own bytes and drawn as the browser shapes it —
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; where lines break and pages end is the
252
- layout's, and identical. Images are decoded from their bytes — no `data:` URIs, so a host page's
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 marking is painted across every page, the way the PDF
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; and the pages are
266
- painted at whatever zoom the reader happened to leave them, so printing the page puts the viewer's
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**, a strip across the top
284
- of the sheet carrying a short label — "Could not render the report", "Could not update the report — showing the previous version", or "Could not export PDF" — and the error's own message. It replaces rather than stacks, is
285
- dismissed by its own button, and is cleared by the next render that lands on the sheet. A successful
286
- export leaves it up, because the panel describes what you are looking at and a download says
287
- nothing about that.
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; nothing is
290
- rethrown to the platform behind it. Failures after the element is removed are not reported, and a
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) }}]: ...`. Showing it is safe because report definitions are
295
- trusted configuration; report _data_ never is, and none of it appears in the path. What can carry
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; `"auto"` follows
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. Free, unlimited, watermarked evaluation; per-developer
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; it is verified offline:
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.8.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.0",
48
- "@quario/layout": "^0.5.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": "^2.9.1",
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.8.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.8.0"
62
+ "quario": "^0.9.0"
63
63
  },
64
64
  "size-limit": [
65
65
  {