@quario/viewer 0.5.0 → 0.6.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 +22 -0
- package/README.md +19 -11
- package/lib/index.d.ts +5 -4
- package/lib/index.js +14 -5
- package/lib/stage.js +34 -7
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2026-09-07
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Numbers presented through `format` now show a fixed two fraction digits**,
|
|
15
|
+
the same as every other target: `1,000.00` where the preview showed `1,000`,
|
|
16
|
+
`21.00%` where it showed `21%`, and a currency's own minor units in place of
|
|
17
|
+
a universal two. The digits come from the engine, so a page on screen and
|
|
18
|
+
the PDF of it agree. A formatted cell is also up to three characters wider,
|
|
19
|
+
so a line that just fitted can wrap and move a page break.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **A page the browser cannot fully paint no longer fails the render.** An
|
|
24
|
+
image whose pixel data is corrupt past the size in its header left that page
|
|
25
|
+
blank _and_ rejected `renderComplete`, so a host awaiting it saw an unhandled
|
|
26
|
+
rejection instead of an answer, and every page after it on screen went
|
|
27
|
+
unpainted too. Such an image is now drawn as nothing and the page is drawn
|
|
28
|
+
around it, marking and all; `renderComplete` resolves `true`, `rendered`
|
|
29
|
+
fires, and no `error` event is raised. It never rejects for this reason
|
|
30
|
+
again: a page that cannot be drawn is blank, not a failed render.
|
|
31
|
+
|
|
10
32
|
## [0.5.0] - 2026-09-05
|
|
11
33
|
|
|
12
34
|
### Changed
|
package/README.md
CHANGED
|
@@ -97,13 +97,20 @@ non-bubbling, like `<img>`'s. Listen on the element. (Because the event is named
|
|
|
97
97
|
inline `onerror` attribute on the element would fire too; `window.onerror` never sees it.)
|
|
98
98
|
|
|
99
99
|
`renderComplete` awaits the newest render settling: `true` when it landed on the sheet with the pages
|
|
100
|
-
on screen
|
|
101
|
-
arrive on the `error` event. `rendered` fires at that same moment. Like every
|
|
102
|
-
answers for the newest render only. A superseded render's failure is reported to no
|
|
100
|
+
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
|
|
103
|
+
one.
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
An image the browser cannot decode — pixel data corrupt past the size in its header, which is all
|
|
106
|
+
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.
|
|
110
|
+
|
|
111
|
+
Your own mistakes surface on the same channel: a report that is not compiled, or a malformed
|
|
112
|
+
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.
|
|
107
114
|
|
|
108
115
|
## Lifecycle
|
|
109
116
|
|
|
@@ -250,7 +257,8 @@ export marks every page: it rides on the layout, so what you see is what the doc
|
|
|
250
257
|
licensed render carries none.
|
|
251
258
|
|
|
252
259
|
While a render is in flight a thin indeterminate bar sits on the toolbar's bottom edge, and the
|
|
253
|
-
viewer
|
|
260
|
+
viewer's inner container — the `.qv-viewer` div in its shadow root, not the element itself —
|
|
261
|
+
reads `aria-busy="true"`. It reports that the viewer is working, not how far along. The
|
|
254
262
|
engine streams events and cannot know how many are still coming.
|
|
255
263
|
|
|
256
264
|
There is no Print button, because the sheet is the wrong thing to print. Your page's stylesheets do
|
|
@@ -261,9 +269,9 @@ document, the same bytes the PDF export hands over. A host that wants its own Pr
|
|
|
261
269
|
two lines:
|
|
262
270
|
|
|
263
271
|
```js
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
const bytes = await
|
|
272
|
+
// The same compiled report and data the element holds, through the pdf()
|
|
273
|
+
// target from the quick start — so what prints is exactly the PDF export.
|
|
274
|
+
const bytes = await view.report.render(pdf(), view.data);
|
|
267
275
|
window.open(URL.createObjectURL(new Blob([bytes], { type: "application/pdf" })));
|
|
268
276
|
```
|
|
269
277
|
|
|
@@ -273,7 +281,7 @@ the export is, watermark and all.
|
|
|
273
281
|
## Errors
|
|
274
282
|
|
|
275
283
|
When a render or an export fails, the viewer says so on the **error panel**, a strip across the top
|
|
276
|
-
of the sheet carrying a short label and the error's own message. It replaces rather than stacks, is
|
|
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
|
|
277
285
|
dismissed by its own button, and is cleared by the next render that lands on the sheet. A successful
|
|
278
286
|
export leaves it up, because the panel describes what you are looking at and a download says
|
|
279
287
|
nothing about that.
|
package/lib/index.d.ts
CHANGED
|
@@ -97,10 +97,11 @@ export class QuarioViewer extends LitElement {
|
|
|
97
97
|
colorScheme: "light" | "dark" | "auto" | undefined;
|
|
98
98
|
/**
|
|
99
99
|
* The newest render settling: `true` when it landed on the sheet with the
|
|
100
|
-
* pages on screen
|
|
101
|
-
* render.
|
|
102
|
-
*
|
|
103
|
-
*
|
|
100
|
+
* pages on screen finished trying to paint, `false` when it failed or there
|
|
101
|
+
* was nothing to render. A page the browser could not draw is blank and the
|
|
102
|
+
* render still landed. Never rejects — failures are the
|
|
103
|
+
* `error` event's — and like every outcome here it answers for the newest
|
|
104
|
+
* render only. `rendered` fires at that same moment.
|
|
104
105
|
*/
|
|
105
106
|
get renderComplete(): Promise<boolean>;
|
|
106
107
|
|
package/lib/index.js
CHANGED
|
@@ -112,7 +112,13 @@ export class QuarioViewer extends LitElement {
|
|
|
112
112
|
#exporting = new Set();
|
|
113
113
|
/** @type {(() => void) | undefined} */
|
|
114
114
|
#unwatch;
|
|
115
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* The newest swap's paint, which `renderComplete` waits behind. Bare, with
|
|
117
|
+
* no catch of its own: the stage settles a page it cannot draw rather than
|
|
118
|
+
* rejecting, so the chain below is reached whatever the pixels did.
|
|
119
|
+
*
|
|
120
|
+
* @type {Promise<void>}
|
|
121
|
+
*/
|
|
116
122
|
#painting = Promise.resolve();
|
|
117
123
|
|
|
118
124
|
// The whole async pipeline: keyed on the render properties, re-run when one
|
|
@@ -186,10 +192,13 @@ export class QuarioViewer extends LitElement {
|
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
/**
|
|
189
|
-
* The newest render settling: `true` when it landed on the sheet
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
195
|
+
* The newest render settling: `true` when it landed on the sheet — after
|
|
196
|
+
* its pages have finished trying to paint — `false` when it failed or there
|
|
197
|
+
* was nothing to render. A page the browser could not draw is blank and the
|
|
198
|
+
* render still landed, so `true` promises the sheet is done changing, not
|
|
199
|
+
* that every page carries pixels. It never rejects — a failed
|
|
200
|
+
* render is handled, on the panel and through the error event — and like
|
|
201
|
+
* every outcome here it answers for the newest render only.
|
|
193
202
|
*
|
|
194
203
|
* @returns {Promise<boolean>}
|
|
195
204
|
*/
|
package/lib/stage.js
CHANGED
|
@@ -21,6 +21,19 @@
|
|
|
21
21
|
* asked a thousand-page report for gigabytes of backing store, and past what
|
|
22
22
|
* the browser would grant the pages simply came up blank.
|
|
23
23
|
*
|
|
24
|
+
* **A page the browser will not paint is blank, not a failure.** `start`
|
|
25
|
+
* swallows and nothing here rejects: the report laid out and the list reached
|
|
26
|
+
* the sheet, which is all a render promised, so the surfaces have no channel
|
|
27
|
+
* for it and deliberately grow none.
|
|
28
|
+
*
|
|
29
|
+
* Little reaches it. An image that will not decode is the layout's own to
|
|
30
|
+
* absorb — `paint` draws it as nothing and draws the page around it — and a
|
|
31
|
+
* face is parsed with fontkit while the report is measured, so bytes that will
|
|
32
|
+
* not parse are a render error long before the sheet. What is left is the face
|
|
33
|
+
* fontkit accepted and the browser refuses, which the browser suite drives by
|
|
34
|
+
* making `FontFace.prototype.load` reject under a record handed straight to
|
|
35
|
+
* `swap`.
|
|
36
|
+
*
|
|
24
37
|
* **A canvas on the sheet is never re-pointed at a second paint.** `paint()`
|
|
25
38
|
* awaits before it draws, so a canvas whose paint is still in flight is
|
|
26
39
|
* *retired* rather than painted over or emptied: it comes off the sheet, its
|
|
@@ -287,6 +300,20 @@ export let stage = () => {
|
|
|
287
300
|
* the next pass over the reach tries again instead of counting the page as
|
|
288
301
|
* painted; one that settles joins `settled`.
|
|
289
302
|
*
|
|
303
|
+
* **The paint this answers with never rejects**, which is the whole of why
|
|
304
|
+
* `swap` does not either. A page that could not be drawn is not a render
|
|
305
|
+
* that failed: the report laid out and the list reached the sheet, so there
|
|
306
|
+
* is nothing for the error panel to say and nothing to report through the
|
|
307
|
+
* error event. Swallowed here, the one place a paint begins, so no caller
|
|
308
|
+
* has to remember to; one bad page then costs its own pixels rather than
|
|
309
|
+
* every page after it in the reach, which this loop awaits one at a time.
|
|
310
|
+
*
|
|
311
|
+
* See the header for how little can still reach it — the layout absorbs a
|
|
312
|
+
* bad image and refuses a bad face while measuring — and why it stays
|
|
313
|
+
* anyway. A paint that failed is forgotten so the next pass over the reach
|
|
314
|
+
* tries again, which is what makes a failure for a passing reason
|
|
315
|
+
* recoverable.
|
|
316
|
+
*
|
|
290
317
|
* @type {(i: number) => Promise<void>}
|
|
291
318
|
*/
|
|
292
319
|
let start = (i) => {
|
|
@@ -302,9 +329,8 @@ export let stage = () => {
|
|
|
302
329
|
() => {
|
|
303
330
|
settled.add(canvas);
|
|
304
331
|
},
|
|
305
|
-
(
|
|
332
|
+
() => {
|
|
306
333
|
backed.delete(canvas);
|
|
307
|
-
throw failure;
|
|
308
334
|
},
|
|
309
335
|
);
|
|
310
336
|
backed.set(canvas, { scale, painted });
|
|
@@ -422,9 +448,8 @@ export let stage = () => {
|
|
|
422
448
|
* keeps no epoch of its own, so a swap painting behind it is never cut off
|
|
423
449
|
* part-painted, and a page it starts is one a swap arriving at the same page
|
|
424
450
|
* waits behind rather than skips. Nothing awaits these paints here: a scroll
|
|
425
|
-
* is not a render and
|
|
426
|
-
*
|
|
427
|
-
* any other paint does.
|
|
451
|
+
* is not a render, and a page that will not paint is blank whoever asked for
|
|
452
|
+
* it — `start` settles either way, so there is nothing here to catch.
|
|
428
453
|
*/
|
|
429
454
|
let pending = false;
|
|
430
455
|
let follow = () => {
|
|
@@ -433,7 +458,7 @@ export let stage = () => {
|
|
|
433
458
|
requestAnimationFrame(() => {
|
|
434
459
|
pending = false;
|
|
435
460
|
if (!list) return;
|
|
436
|
-
for (let i of keep()) paintPage(i)
|
|
461
|
+
for (let i of keep()) void paintPage(i);
|
|
437
462
|
});
|
|
438
463
|
};
|
|
439
464
|
scroll.addEventListener("scroll", follow);
|
|
@@ -510,7 +535,9 @@ export let stage = () => {
|
|
|
510
535
|
* — and the reach is read from those offsets, so it is chosen after they
|
|
511
536
|
* are in. What the
|
|
512
537
|
* returned promise settles behind is the reach, which is what the caller's
|
|
513
|
-
* `renderComplete` means by "the pages on screen
|
|
538
|
+
* `renderComplete` means by "the pages on screen have finished trying to
|
|
539
|
+
* paint" — and **it never rejects**, because `start` swallows a page the
|
|
540
|
+
* browser will not draw and the reach walks on past it.
|
|
514
541
|
*/
|
|
515
542
|
swap: async (next, faces) => {
|
|
516
543
|
// Reading the offsets flushes layout, so only an actual reswap pays for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quario/viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@lit/task": "^1.0.3",
|
|
47
|
-
"@quario/layout": "^0.
|
|
47
|
+
"@quario/layout": "^0.3.0",
|
|
48
48
|
"lit": "^3.3.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
@@ -53,12 +53,12 @@
|
|
|
53
53
|
"@size-limit/preset-small-lib": "^13.0.3",
|
|
54
54
|
"esbuild": "^0.28.2",
|
|
55
55
|
"exceljs": "^4.4.0",
|
|
56
|
-
"quario": "^0.
|
|
56
|
+
"quario": "^0.6.0",
|
|
57
57
|
"size-limit": "^13.0.3",
|
|
58
58
|
"typescript": "^7.0.2"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"quario": "^0.
|
|
61
|
+
"quario": "^0.6.0"
|
|
62
62
|
},
|
|
63
63
|
"size-limit": [
|
|
64
64
|
{
|