@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/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
- * Sheet geometry, in PostScript points — the pdf target's `PdfPage`
6
- * vocabulary, because the sheet stands in visually for the page configured
7
- * there. Purely visual: pass the same values to `pdf({ page })` so preview
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 interface ViewerPage {
11
- /** Named size or `[width, height]` in points. Default `'A4'`. */
12
- size?: "A4" | "letter" | [number, number];
13
- /** Margin on all four sides, in points. Default `54` (0.75 in). */
14
- margin?: number;
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 reached
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. Importing this module defines nothing
44
- * import `@quario/viewer/register` for the one-line define, or call
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 missing `"html"` target, a malformed option property)
49
- * throw a `TypeError` naming the property, delivered through the `error`
50
- * event and the error panel like every other failure; a report that could
51
- * not be rendered leaves the element up with the panel saying why. A
52
- * superseded render's failure is reported to no one.
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 configured targets, as passed to `report.render` one must be named
65
- * `"html"` to display; ones named `"pdf"`/`"xlsx"`/`"csv"` become
66
- * export buttons, in array order.
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
- * the sheet to the viewer's width and is shrink-only — it never enlarges
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
- /** Sheet geometry. The preview approximates; exports are exact. */
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 reached the sheet, `false`
93
- * when it failed or there was nothing to render. Never rejects — failures
94
- * are the `error` event's — and like every outcome here it answers for
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 reached the sheet — the mount and every update. */
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 through
5
- * properties, displays the html target's fragment on a shadow-DOM sheet, and
6
- * offers export downloads for whichever exportable targets the host passed.
7
- * Outcomes reach the host as `rendered` and `error` events, plus
8
- * `renderComplete`, the one awaitable for the newest render settling.
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
- * Escaping stays exclusively the html target's job (its documented output is
15
- * injected the documented way), and the viewer adds no report content of its
16
- * own. The unlicensed marking is the one thing it presents rather than merely
17
- * styles: the wording still arrives inside the fragment, and the stage draws
18
- * repeating watermarks on the scaled sheet — see
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 { display, geometry, level, name, scheme } from "./check.js";
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 { REPORT } from "./style.js";
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`) and listen for `rendered`
56
- * and `error`; rapid successive writes render only the newest state, because
57
- * the render pipeline is a task keyed on those properties and the task's own
58
- * call-id guard drops every superseded run whole — including its failure,
59
- * which is reported to no one (ADR 0005's narrowed guarantee).
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, REPORT];
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 reached the sheet — the mount/update boundary. */
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. The task's call-id guard is the latest-wins machinery; side
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
- let target = display(report, targets);
124
- let fragment = await /** @type {any} */ (report).render(target, data);
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.#drop(signal)) return this.#abandon();
129
- return fragment;
138
+ if (signal.aborted && !this.isConnected) return this.#abandon();
139
+ return { list, fonts };
130
140
  },
131
- onComplete: (fragment) => {
132
- if (fragment === null || !this.isConnected) return;
133
- this.#stage.swap(fragment);
134
- this.#landed = true;
135
- this.#stale = false;
136
- // A render that reached the sheet takes the panel down: the panel says
137
- // what is wrong with what the reader is looking at, and this is the
138
- // moment that stops being true. A successful export is not that moment.
139
- this.#failure = undefined;
140
- this.#dismissed = false;
141
- this.dispatchEvent(new CustomEvent("rendered"));
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 reached the sheet, `false`
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.#watchStage();
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 { type, label: format } = EXPORTS[target.name];
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(() => download(body, this.#name + "." + target.name, type));
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 magnifier trigger in the bar and the popover it opens —
3
- * "Fit sheet" above a separator, then the percentages `zoom.js` offers. It is
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 sheet" is checked whenever the
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: 13px;
93
- height: 13px;
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 ? tick : ""}</span>
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: magnifier,
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 sheet", "fit", mode === "fit", choose)}
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", label: "×", click: dismiss })}
125
+ ${button({ title: "Dismiss", content: x, click: dismiss })}
125
126
  </div>
126
127
  `;