@quario/viewer 0.3.0 → 0.5.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 +73 -0
- package/README.md +84 -75
- package/lib/button.js +11 -4
- package/lib/check.js +23 -63
- package/lib/chrome.js +22 -4
- package/lib/icons.js +25 -0
- package/lib/index.d.ts +37 -29
- package/lib/index.js +68 -63
- package/lib/menu.js +20 -33
- package/lib/panel.js +2 -1
- package/lib/stage.js +410 -235
- package/lib/toolbar.js +17 -36
- package/package.json +6 -4
- package/lib/mark.js +0 -40
- package/lib/style.js +0 -123
package/lib/index.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import type { LitElement } from "lit";
|
|
2
|
+
import type { LayoutFonts, LayoutPage } from "@quario/layout";
|
|
2
3
|
import type { CompiledReport, Target } from "quario";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* and export agree.
|
|
6
|
+
* Page geometry, in PostScript points — the layout's own vocabulary, which
|
|
7
|
+
* the pdf target's `page` shares. The sheet is laid out on it: pass the same
|
|
8
|
+
* values to `pdf({ page })` and the preview pages where the document pages.
|
|
9
9
|
*/
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
export type ViewerPage = LayoutPage;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The font mapping: a declared `family` name to TrueType bytes, the same
|
|
14
|
+
* record a host passes to `pdf({ fonts })`. The layout measures against
|
|
15
|
+
* them and the sheet draws in them.
|
|
16
|
+
*/
|
|
17
|
+
export type ViewerFonts = LayoutFonts;
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* A compiled report, from a quario instance's `report()`. The instance —
|
|
@@ -29,8 +31,8 @@ export interface ViewerErrorDetail {
|
|
|
29
31
|
/** The caught value, exactly as thrown. */
|
|
30
32
|
error: unknown;
|
|
31
33
|
/**
|
|
32
|
-
* Which failure occurred: `mount-render` until a render has ever
|
|
33
|
-
* the sheet, `update-render` after, `export` for a download that could not
|
|
34
|
+
* Which failure occurred: `mount-render` until a render has ever landed
|
|
35
|
+
* on the sheet, `update-render` after, `export` for a download that could not
|
|
34
36
|
* be produced.
|
|
35
37
|
*/
|
|
36
38
|
kind: ViewerErrorKind;
|
|
@@ -40,16 +42,18 @@ export interface ViewerErrorDetail {
|
|
|
40
42
|
* The embeddable report viewer, `<quario-viewer>`. Assign `report`,
|
|
41
43
|
* `targets` and `data` as properties (they are values no attribute could
|
|
42
44
|
* carry) and listen for `rendered` and `error`; rapid successive writes
|
|
43
|
-
* render only the newest state.
|
|
44
|
-
*
|
|
45
|
+
* render only the newest state. The sheet is the report laid out on pages
|
|
46
|
+
* by `@quario/layout` — the list the pdf target writes — painted on one
|
|
47
|
+
* canvas per page. Importing this module defines nothing — import
|
|
48
|
+
* `@quario/viewer/register` for the one-line define, or call
|
|
45
49
|
* `customElements.define` with a tag of your own.
|
|
46
50
|
*
|
|
47
51
|
* Failures split along one line: the host's own mistakes (a report that is
|
|
48
|
-
* not compiled, a
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
52
|
+
* not compiled, a malformed option property) throw a `TypeError` naming the
|
|
53
|
+
* property, delivered through the `error` event and the error panel like
|
|
54
|
+
* every other failure; a report that could not be rendered leaves the
|
|
55
|
+
* element up with the panel saying why. A superseded render's failure is
|
|
56
|
+
* reported to no one.
|
|
53
57
|
*
|
|
54
58
|
* Removing the element abandons in-flight work and releases its observers;
|
|
55
59
|
* the properties persist, and reconnecting re-renders from them. There is no
|
|
@@ -61,16 +65,17 @@ export class QuarioViewer extends LitElement {
|
|
|
61
65
|
/** The compiled report to display and export. */
|
|
62
66
|
report: ViewableReport | undefined;
|
|
63
67
|
/**
|
|
64
|
-
* The
|
|
65
|
-
* `"
|
|
66
|
-
*
|
|
68
|
+
* The export targets, as passed to `report.render`: ones named
|
|
69
|
+
* `"pdf"`/`"xlsx"`/`"csv"` become export buttons, in array order. The
|
|
70
|
+
* sheet needs none of them — it is the layout's own. An empty array is
|
|
71
|
+
* a viewer with nothing to export.
|
|
67
72
|
*/
|
|
68
73
|
targets: readonly Target[] | undefined;
|
|
69
74
|
/** The render document. Assigning re-renders; the newest write wins. */
|
|
70
75
|
data: unknown;
|
|
71
76
|
/**
|
|
72
77
|
* The zoom to open at: `'fit'` (the default) or a percentage. Fit sizes
|
|
73
|
-
*
|
|
78
|
+
* one page to the viewer's width and is shrink-only — it never enlarges
|
|
74
79
|
* past 100%, so a viewer with room to spare shows the report at its true
|
|
75
80
|
* point size. An authored percentage must be between 25 and 200, and is
|
|
76
81
|
* continuous in that range rather than one of the zoom menu's own stops;
|
|
@@ -79,8 +84,10 @@ export class QuarioViewer extends LitElement {
|
|
|
79
84
|
* computes them.
|
|
80
85
|
*/
|
|
81
86
|
zoom: "fit" | number | undefined;
|
|
82
|
-
/**
|
|
87
|
+
/** Page geometry. Pass the same values to `pdf({ page })`. */
|
|
83
88
|
page: ViewerPage | undefined;
|
|
89
|
+
/** Font mapping. Pass the same record to `pdf({ fonts })`. */
|
|
90
|
+
fonts: ViewerFonts | undefined;
|
|
84
91
|
/** Export download name, without extension. Default `'report'`. */
|
|
85
92
|
filename: string | undefined;
|
|
86
93
|
/**
|
|
@@ -89,10 +96,11 @@ export class QuarioViewer extends LitElement {
|
|
|
89
96
|
*/
|
|
90
97
|
colorScheme: "light" | "dark" | "auto" | undefined;
|
|
91
98
|
/**
|
|
92
|
-
* The newest render settling: `true` when it
|
|
93
|
-
* when it failed or there was nothing to
|
|
94
|
-
* are the `error` event's — and like every
|
|
95
|
-
* the newest render only.
|
|
99
|
+
* The newest render settling: `true` when it landed on the sheet with the
|
|
100
|
+
* pages on screen painted, `false` when it failed or there was nothing to
|
|
101
|
+
* render. Never rejects — failures are the `error` event's — and like every
|
|
102
|
+
* outcome here it answers for the newest render only. `rendered` fires at
|
|
103
|
+
* that same moment.
|
|
96
104
|
*/
|
|
97
105
|
get renderComplete(): Promise<boolean>;
|
|
98
106
|
|
|
@@ -120,7 +128,7 @@ export class QuarioViewer extends LitElement {
|
|
|
120
128
|
|
|
121
129
|
/** The element's events. Both are non-bubbling, like `<img>`'s. */
|
|
122
130
|
export interface QuarioViewerEventMap {
|
|
123
|
-
/** A render
|
|
131
|
+
/** A render landed on the sheet — the mount and every update. */
|
|
124
132
|
rendered: CustomEvent<undefined>;
|
|
125
133
|
/** A failure the host should know about; `detail.kind` says which. */
|
|
126
134
|
error: CustomEvent<ViewerErrorDetail>;
|
package/lib/index.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @quario/viewer — the embeddable viewer element. Not a render target: it
|
|
3
3
|
* compiles nothing and never touches the event stream. `<quario-viewer>`
|
|
4
|
-
* consumes a compiled report plus the host's configured targets
|
|
5
|
-
* properties,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* consumes a compiled report plus the host's configured export targets
|
|
5
|
+
* through properties, lays the report out on pages through `@quario/layout`
|
|
6
|
+
* and paints them on a shadow-DOM sheet — the same list the pdf target
|
|
7
|
+
* writes, so the preview breaks its pages where the document does
|
|
8
|
+
* (docs/adr/0039) — and offers export downloads for whichever exportable
|
|
9
|
+
* targets the host passed. Outcomes reach the host as `rendered` and `error`
|
|
10
|
+
* events, plus `renderComplete`, the one awaitable for the newest render
|
|
11
|
+
* settling.
|
|
9
12
|
*
|
|
10
13
|
* Importing this module defines nothing: the element class is the export,
|
|
11
14
|
* and `@quario/viewer/register` performs the one-line define for hosts that
|
|
12
15
|
* want it (docs/adr/0005-the-surfaces-are-custom-elements.md).
|
|
13
16
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* docs/adr/0017-the-viewer-watermarks-the-sheet.md.
|
|
17
|
+
* Nothing here is markup: the sheet is a stack of canvases, and the list's
|
|
18
|
+
* text reaches them as strings the painter draws, so the viewer has no
|
|
19
|
+
* markup edge and adds no report content of its own. The unlicensed marking
|
|
20
|
+
* rides on the list, one op per page, painted last — the same placement the
|
|
21
|
+
* pdf target writes (docs/adr/0017-the-viewer-watermarks-the-sheet.md).
|
|
20
22
|
*
|
|
21
23
|
* The error panel is the only wording the viewer authors, and it is not
|
|
22
24
|
* report content: it names which failure occurred, because no error knows
|
|
@@ -24,14 +26,14 @@
|
|
|
24
26
|
*/
|
|
25
27
|
import { Task, TaskStatus } from "@lit/task";
|
|
26
28
|
import { LitElement, html } from "lit";
|
|
29
|
+
import { layout } from "@quario/layout";
|
|
27
30
|
import { BUTTON } from "./button.js";
|
|
28
31
|
import { CHROME, progress } from "./chrome.js";
|
|
29
|
-
import {
|
|
32
|
+
import { exports, geometry, level, name, scheme } from "./check.js";
|
|
30
33
|
import { MENU, zoomMenu } from "./menu.js";
|
|
31
34
|
import { PANEL, label, panel } from "./panel.js";
|
|
32
35
|
import { SURFACE, stage } from "./stage.js";
|
|
33
|
-
import {
|
|
34
|
-
import { EXPORTS, download, exportGroup } from "./toolbar.js";
|
|
36
|
+
import { EXPORTS, exportGroup, saveAs } from "./toolbar.js";
|
|
35
37
|
import { wanted } from "./zoom.js";
|
|
36
38
|
|
|
37
39
|
/** @type {(report: unknown, targets: unknown) => boolean} */
|
|
@@ -45,18 +47,19 @@ let said = (error) => {
|
|
|
45
47
|
|
|
46
48
|
// The properties are described once, in the hand-written public declarations,
|
|
47
49
|
// and read back here — a second copy in JSDoc is a copy that drifts.
|
|
48
|
-
/** @import { ViewableReport, ViewerPage } from './index.d.ts' */
|
|
50
|
+
/** @import { ViewableReport, ViewerFonts, ViewerPage } from './index.d.ts' */
|
|
49
51
|
|
|
50
52
|
/** The tag `@quario/viewer/register` defines the element under. */
|
|
51
53
|
export const TAG = "quario-viewer";
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
56
|
* The viewer element. Hosts assign `report`, `targets` and `data` (plus the
|
|
55
|
-
* option properties `zoom`, `page`, `filename` and `colorScheme`)
|
|
56
|
-
* and `error`; rapid successive writes render only
|
|
57
|
-
* the render pipeline is a task keyed on those
|
|
58
|
-
* call-id guard drops every superseded run
|
|
59
|
-
* which is reported to no one (ADR 0005's
|
|
57
|
+
* option properties `zoom`, `page`, `fonts`, `filename` and `colorScheme`)
|
|
58
|
+
* and listen for `rendered` and `error`; rapid successive writes render only
|
|
59
|
+
* the newest state, because the render pipeline is a task keyed on those
|
|
60
|
+
* properties and the task's own call-id guard drops every superseded run
|
|
61
|
+
* whole — including its failure, which is reported to no one (ADR 0005's
|
|
62
|
+
* narrowed guarantee).
|
|
60
63
|
*
|
|
61
64
|
* Removal is not destruction: disconnecting abandons in-flight work and
|
|
62
65
|
* releases the resize observer, the properties persist, and reconnecting
|
|
@@ -66,7 +69,7 @@ export class QuarioViewer extends LitElement {
|
|
|
66
69
|
// CHROME declares the default palette every other sheet paints from, so it
|
|
67
70
|
// has to be in this list -- but anywhere in it: a custom property resolves
|
|
68
71
|
// down the inherited chain, not by stylesheet order.
|
|
69
|
-
static styles = [CHROME, BUTTON, MENU, SURFACE, PANEL
|
|
72
|
+
static styles = [CHROME, BUTTON, MENU, SURFACE, PANEL];
|
|
70
73
|
|
|
71
74
|
// Properties only, no attributes: `report`, `targets`, `data` and `page`
|
|
72
75
|
// are values no attribute could carry, and one door beats two.
|
|
@@ -76,6 +79,7 @@ export class QuarioViewer extends LitElement {
|
|
|
76
79
|
data: { attribute: false },
|
|
77
80
|
zoom: { attribute: false },
|
|
78
81
|
page: { attribute: false },
|
|
82
|
+
fonts: { attribute: false },
|
|
79
83
|
filename: { attribute: false },
|
|
80
84
|
colorScheme: { attribute: false },
|
|
81
85
|
};
|
|
@@ -85,7 +89,7 @@ export class QuarioViewer extends LitElement {
|
|
|
85
89
|
// fails and a disconnect that does not. The template interpolates its
|
|
86
90
|
// element as a node, which lit-html leaves untouched across re-renders.
|
|
87
91
|
#stage = stage();
|
|
88
|
-
/** Whether a render has ever
|
|
92
|
+
/** Whether a render has ever landed on the sheet — the mount/update boundary. */
|
|
89
93
|
#landed = false;
|
|
90
94
|
/** Whether the sheet stopped matching the properties while disconnected. */
|
|
91
95
|
#stale = false;
|
|
@@ -108,37 +112,49 @@ export class QuarioViewer extends LitElement {
|
|
|
108
112
|
#exporting = new Set();
|
|
109
113
|
/** @type {(() => void) | undefined} */
|
|
110
114
|
#unwatch;
|
|
115
|
+
/** @type {Promise<void>} The newest swap's paint, which `renderComplete` waits behind. */
|
|
116
|
+
#painting = Promise.resolve();
|
|
111
117
|
|
|
112
118
|
// The whole async pipeline: keyed on the render properties, re-run when one
|
|
113
|
-
// changes
|
|
119
|
+
// changes — `page` and `fonts` among them, since either moves where the
|
|
120
|
+
// pages break. The task's call-id guard is the latest-wins machinery; side
|
|
114
121
|
// effects — the stage swap and both events — live only in the callbacks
|
|
115
122
|
// below, which that guard restricts to the newest run, never in the task
|
|
116
123
|
// body a stale run still executes to completion. `null` is the "nothing to
|
|
117
124
|
// show" result: unlike the task primitive's own initial-state symbol it
|
|
118
125
|
// settles `taskComplete`, which is what lets `renderComplete` always answer.
|
|
119
126
|
#task = new Task(this, {
|
|
120
|
-
args: () => [this.report, this.targets, this.data, this.#epoch],
|
|
121
|
-
task: async ([report, targets, data], { signal }) => {
|
|
127
|
+
args: () => [this.report, this.targets, this.data, this.page, this.fonts, this.#epoch],
|
|
128
|
+
task: async ([report, targets, data, page, fonts], { signal }) => {
|
|
122
129
|
if (!this.#begin(report, targets)) return null;
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
// The sheet's own target: the layout list, on the host's page and
|
|
131
|
+
// fonts — the same two a host passes to `pdf()`, which is what makes
|
|
132
|
+
// the preview page where the document pages.
|
|
133
|
+
let target = layout({ page: /** @type {any} */ (page), fonts: /** @type {any} */ (fonts) });
|
|
134
|
+
let list = await /** @type {any} */ (report).render(target, data);
|
|
125
135
|
// The engine takes no signal, so abandonment is the guards around this
|
|
126
136
|
// body; the check only spares the swap when the answer arrives after a
|
|
127
137
|
// disconnect mid-render.
|
|
128
|
-
if (this
|
|
129
|
-
return
|
|
138
|
+
if (signal.aborted && !this.isConnected) return this.#abandon();
|
|
139
|
+
return { list, fonts };
|
|
130
140
|
},
|
|
131
|
-
onComplete: (
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
this.#
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
onComplete: (result) => {
|
|
142
|
+
if (result === null || !this.isConnected) return;
|
|
143
|
+
// The swap sizes every page before it settles, so the reader's place
|
|
144
|
+
// is held; the paint it awaits is what `rendered` waits for.
|
|
145
|
+
this.#painting = this.#stage.swap(result.list, result.fonts).then(() => {
|
|
146
|
+
this.#landed = true;
|
|
147
|
+
this.#stale = false;
|
|
148
|
+
// A render that landed on the sheet takes the panel down: the panel
|
|
149
|
+
// says what is wrong with what the reader is looking at, and this is
|
|
150
|
+
// the moment that stops being true. A successful export is not that
|
|
151
|
+
// moment.
|
|
152
|
+
this.#failure = undefined;
|
|
153
|
+
this.#dismissed = false;
|
|
154
|
+
// After the paint, so the update the task queued has already run.
|
|
155
|
+
this.requestUpdate();
|
|
156
|
+
this.dispatchEvent(new CustomEvent("rendered"));
|
|
157
|
+
});
|
|
142
158
|
},
|
|
143
159
|
onError: (error) => {
|
|
144
160
|
if (!this.isConnected) return;
|
|
@@ -161,6 +177,8 @@ export class QuarioViewer extends LitElement {
|
|
|
161
177
|
this.zoom = undefined;
|
|
162
178
|
/** @type {ViewerPage | undefined} */
|
|
163
179
|
this.page = undefined;
|
|
180
|
+
/** @type {ViewerFonts | undefined} */
|
|
181
|
+
this.fonts = undefined;
|
|
164
182
|
/** @type {string | undefined} */
|
|
165
183
|
this.filename = undefined;
|
|
166
184
|
/** @type {"light" | "dark" | "auto" | undefined} */
|
|
@@ -168,7 +186,7 @@ export class QuarioViewer extends LitElement {
|
|
|
168
186
|
}
|
|
169
187
|
|
|
170
188
|
/**
|
|
171
|
-
* The newest render settling: `true` when it
|
|
189
|
+
* The newest render settling: `true` when it landed on the sheet, `false`
|
|
172
190
|
* when it failed or there was nothing to render. It never rejects — a
|
|
173
191
|
* failed render is handled, on the panel and through the error event — and
|
|
174
192
|
* like every outcome here it answers for the newest render only.
|
|
@@ -180,7 +198,7 @@ export class QuarioViewer extends LitElement {
|
|
|
180
198
|
this.#task.status === TaskStatus.INITIAL
|
|
181
199
|
? this.#landed
|
|
182
200
|
: this.#task.taskComplete.then(
|
|
183
|
-
() => this.#landed,
|
|
201
|
+
() => this.#painting.then(() => this.#landed),
|
|
184
202
|
() => false,
|
|
185
203
|
),
|
|
186
204
|
);
|
|
@@ -220,7 +238,7 @@ export class QuarioViewer extends LitElement {
|
|
|
220
238
|
})}
|
|
221
239
|
</div>
|
|
222
240
|
<div class="qv-body">
|
|
223
|
-
${this.#panel()}
|
|
241
|
+
${this.#failure && !this.#dismissed ? panel(this.#failure, () => this.#dismiss()) : ""}
|
|
224
242
|
${this.#stage.element}
|
|
225
243
|
</div>
|
|
226
244
|
</div>
|
|
@@ -241,7 +259,9 @@ export class QuarioViewer extends LitElement {
|
|
|
241
259
|
super.connectedCallback();
|
|
242
260
|
// A fitted sheet follows the host's box; the observer is released on
|
|
243
261
|
// disconnect, so reconnection takes it again.
|
|
244
|
-
this.#
|
|
262
|
+
this.#unwatch ??= this.#stage.watch(() => {
|
|
263
|
+
if (this.#mode === "fit" && this.#wanted() !== this.#stage.percent()) this.#apply();
|
|
264
|
+
});
|
|
245
265
|
// Reconnect re-renders from the current properties — but only when the
|
|
246
266
|
// sheet stopped matching them: a render was abandoned mid-flight, or a
|
|
247
267
|
// property write landed while disconnected. The task's arguments did not
|
|
@@ -273,14 +293,10 @@ export class QuarioViewer extends LitElement {
|
|
|
273
293
|
if (this.#invalid) throw this.#invalid;
|
|
274
294
|
if (vacant(report, targets)) return null;
|
|
275
295
|
if (!this.isConnected) return this.#abandon();
|
|
296
|
+
exports(report, targets);
|
|
276
297
|
return true;
|
|
277
298
|
}
|
|
278
299
|
|
|
279
|
-
/** @param {AbortSignal} signal */
|
|
280
|
-
#drop(signal) {
|
|
281
|
-
return signal.aborted && !this.isConnected;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
300
|
#abandon() {
|
|
285
301
|
this.#stale = true;
|
|
286
302
|
return null;
|
|
@@ -342,18 +358,6 @@ export class QuarioViewer extends LitElement {
|
|
|
342
358
|
if (said(was) !== said(this.#invalid)) this.#epoch++;
|
|
343
359
|
}
|
|
344
360
|
|
|
345
|
-
#panel() {
|
|
346
|
-
return this.#failure && !this.#dismissed ? panel(this.#failure, () => this.#dismiss()) : "";
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
#onResize() {
|
|
350
|
-
if (this.#mode === "fit" && this.#wanted() !== this.#stage.percent()) this.#apply();
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
#watchStage() {
|
|
354
|
-
this.#unwatch ??= this.#stage.watch(() => this.#onResize());
|
|
355
|
-
}
|
|
356
|
-
|
|
357
361
|
#wake() {
|
|
358
362
|
if (this.#stale || this.#task.status === TaskStatus.PENDING) {
|
|
359
363
|
this.#epoch++;
|
|
@@ -423,12 +427,13 @@ export class QuarioViewer extends LitElement {
|
|
|
423
427
|
* @param {any} target
|
|
424
428
|
*/
|
|
425
429
|
async #export(target) {
|
|
426
|
-
let
|
|
430
|
+
let type = EXPORTS[target.name];
|
|
431
|
+
let format = target.name.toUpperCase();
|
|
427
432
|
this.#exporting.add(target.name);
|
|
428
433
|
this.requestUpdate();
|
|
429
434
|
try {
|
|
430
435
|
let body = await /** @type {any} */ (this.report).render(target, this.data);
|
|
431
|
-
this.#ifConnected(() =>
|
|
436
|
+
this.#ifConnected(() => saveAs(body, this.#name + "." + target.name, type));
|
|
432
437
|
} catch (error) {
|
|
433
438
|
this.#ifConnected(() => this.#announce(error, "export", format));
|
|
434
439
|
} finally {
|
package/lib/menu.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The zoom menu: the
|
|
3
|
-
* "Fit
|
|
2
|
+
* The zoom menu: the search trigger in the bar and the popover it opens —
|
|
3
|
+
* "Fit page" above a separator, then the percentages `zoom.js` offers. It is
|
|
4
4
|
* the whole zoom control; the live percentage is never drawn, it is what the
|
|
5
5
|
* trigger is named after ("Zoom, 62%"), so the bar carries one icon rather
|
|
6
6
|
* than a readout that changes width under the reader.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* Radio semantics, not toggles: exactly one row is the current mode, and
|
|
21
21
|
* choosing the one already checked settles as a no-op in the element's own
|
|
22
22
|
* `#setMode`, so this module reports every row alike. The check follows
|
|
23
|
-
* the mode rather than the measurement — "Fit
|
|
23
|
+
* the mode rather than the measurement — "Fit page" is checked whenever the
|
|
24
24
|
* viewer is fitting, whatever percentage that came out at, and a host-set
|
|
25
25
|
* percentage the menu does not offer checks nothing.
|
|
26
26
|
*
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { css, html } from "lit";
|
|
31
31
|
import { button } from "./button.js";
|
|
32
|
+
import { check, search } from "./icons.js";
|
|
32
33
|
import { STEPS } from "./zoom.js";
|
|
33
34
|
|
|
34
35
|
/** The popover's id, scoped to the shadow root, so two viewers never collide. */
|
|
@@ -85,12 +86,22 @@ export let MENU = css`
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
/* The check's slot is held whether or not a check is in it, so the labels
|
|
88
|
-
line up down the menu.
|
|
89
|
+
line up down the menu. 14px rather than 13: the small icon size serves this
|
|
90
|
+
slot and the editor's gutter alike, which is one size fewer than buying a
|
|
91
|
+
third for one glyph (ADR 0044). */
|
|
89
92
|
.qv-tick {
|
|
90
93
|
display: inline-flex;
|
|
91
94
|
flex: none;
|
|
92
|
-
width:
|
|
93
|
-
height:
|
|
95
|
+
width: 14px;
|
|
96
|
+
height: 14px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Narrower than the chrome's 18px base on specificity, not on where this
|
|
100
|
+
sheet sits in \`static styles\` — reordering that array must not silently
|
|
101
|
+
put the tick back to 18px. */
|
|
102
|
+
.qv-viewer .qv-tick svg {
|
|
103
|
+
width: 14px;
|
|
104
|
+
height: 14px;
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
.qv-separator {
|
|
@@ -100,30 +111,6 @@ export let MENU = css`
|
|
|
100
111
|
}
|
|
101
112
|
`;
|
|
102
113
|
|
|
103
|
-
/** @type {import('lit').TemplateResult} */
|
|
104
|
-
let magnifier = html`
|
|
105
|
-
<svg viewBox="0 0 20 20" width="19" height="19" aria-hidden="true">
|
|
106
|
-
<g fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round">
|
|
107
|
-
<circle cx="8.75" cy="8.75" r="5.25"></circle>
|
|
108
|
-
<path d="M12.6 12.6 16.4 16.4"></path>
|
|
109
|
-
</g>
|
|
110
|
-
</svg>
|
|
111
|
-
`;
|
|
112
|
-
|
|
113
|
-
/** @type {import('lit').TemplateResult} */
|
|
114
|
-
let tick = html`
|
|
115
|
-
<svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true">
|
|
116
|
-
<path
|
|
117
|
-
d="M3.4 8.4 6.4 11.4 12.6 4.8"
|
|
118
|
-
fill="none"
|
|
119
|
-
stroke="currentColor"
|
|
120
|
-
stroke-width="1.9"
|
|
121
|
-
stroke-linecap="round"
|
|
122
|
-
stroke-linejoin="round"
|
|
123
|
-
></path>
|
|
124
|
-
</svg>
|
|
125
|
-
`;
|
|
126
|
-
|
|
127
114
|
/**
|
|
128
115
|
* The button that opens this menu, found by the invoker relationship itself
|
|
129
116
|
* rather than by where it sits: a wrapper added for layout, or a second
|
|
@@ -276,7 +263,7 @@ let row = (label, value, checked, choose) => html`
|
|
|
276
263
|
data-zoom=${String(value)}
|
|
277
264
|
@click=${pick(value, choose)}
|
|
278
265
|
>
|
|
279
|
-
<span class="qv-tick">${checked ?
|
|
266
|
+
<span class="qv-tick">${checked ? check : ""}</span>
|
|
280
267
|
${label}
|
|
281
268
|
</button>
|
|
282
269
|
`;
|
|
@@ -298,7 +285,7 @@ export let zoomMenu = ({ mode, percent, choose }) => html`
|
|
|
298
285
|
${button({
|
|
299
286
|
title: "Zoom, " + percent + "%",
|
|
300
287
|
popover: ID,
|
|
301
|
-
content:
|
|
288
|
+
content: search,
|
|
302
289
|
})}
|
|
303
290
|
<div
|
|
304
291
|
id=${ID}
|
|
@@ -310,7 +297,7 @@ export let zoomMenu = ({ mode, percent, choose }) => html`
|
|
|
310
297
|
@beforetoggle=${leaving}
|
|
311
298
|
@keydown=${navigate}
|
|
312
299
|
>
|
|
313
|
-
${row("Fit
|
|
300
|
+
${row("Fit page", "fit", mode === "fit", choose)}
|
|
314
301
|
<hr class="qv-separator" role="separator" />
|
|
315
302
|
${STEPS.map((step) => row(step + "%", step, mode === step, choose))}
|
|
316
303
|
</div>
|
package/lib/panel.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { css, html } from "lit";
|
|
21
21
|
import { button } from "./button.js";
|
|
22
|
+
import { x } from "./icons.js";
|
|
22
23
|
|
|
23
24
|
export let PANEL = css`
|
|
24
25
|
/* Positioned rather than laid out, for the reason the progress strip is: a
|
|
@@ -121,6 +122,6 @@ export let panel = (failure, dismiss) => html`
|
|
|
121
122
|
<p class="qv-error-label">${failure.label}</p>
|
|
122
123
|
<p class="qv-error-message">${wording(failure.error)}</p>
|
|
123
124
|
</div>
|
|
124
|
-
${button({ title: "Dismiss",
|
|
125
|
+
${button({ title: "Dismiss", content: x, click: dismiss })}
|
|
125
126
|
</div>
|
|
126
127
|
`;
|