@quario/editor
v0.9.1
Published
Tiny, embeddable banded document designer for quario. A custom element that edits the report document itself.
Maintainers
Readme
@quario/editor
The embeddable banded document designer for quario.
<quario-editor> shows a report rendered on your sample data and lets the author rearrange it
structurally. Drag items between bands with a gutter grip, reorder table columns on the table,
restructure groups from a docked list, and edit the selected node's values, styles, templates and
expressions in a properties panel. Every edit recompiles and re-lays out through the real engine
and the real page layout, the one the PDF target writes. What the author sees is therefore the
report as it will page, not an approximation of it.
There is no infinite canvas and no free positioning, because the schema has no concept of either:
bands stack, items stack, tables have columns. Every drag is structural and every drop snaps to a
slot the schema actually has. The editor refuses drops that would break at render time, such as
moving an item that reads the current row (@) out of the detail band.
Install
npm install @quario/editor quarioESM-only, Node 22+ tooling, evergreen browsers. Runtime dependencies:
lit, @lit/task, tinykeys, and
@quario/layout, the paged layout the design surface paints. The engine is not a dependency —
your app passes its own configured instance in, so your bundle carries one copy of it.
Quick start
import { quario } from "quario";
import "@quario/editor/register";
const editor = document.createElement("quario-editor");
editor.instance = quario({ license: "quario_..." });
editor.schema = starterSchema; // the document the author begins with
editor.data = sampleData; // what the design surface is laid out on
editor.page = { size: "A4", margin: 54 }; // the same values you pass pdf({ page })
editor.addEventListener("change", ({ detail }) => {
save(detail.schema); // deep-frozen; assigning it back to `schema` is a no-op
saveButton.disabled = detail.problems.length > 0;
});
editor.addEventListener("error", () => {
// Nothing could be rendered, so the button above may be answering for an
// older document — it is right again on the next `change`.
saveButton.disabled = true;
});
document.body.append(editor);Every rectangle on the surface maps back to the schema node it draws, which is how clicking the rendered report selects the node behind it — no target to configure for it.
The element
The editor is uncontrolled: schema seeds it, and the editor owns the document from there.
Only assigning a new schema object resets the document, history and selection. Assigning
back the exact object the last change delivered does nothing, so a naive persist-and-restore
loop cannot wipe the author's undo history. Assigning a new instance, functions, data, page
or fonts recompiles and re-lays out while leaving the author's work untouched — a license key
arriving mid-session is one property write, not a catastrophe.
| Property | What it is |
| ------------- | ------------------------------------------------------------------------------- |
| schema | The starter report document. A new object identity loads and resets. |
| instance | Your configured quario() instance. The license rides on it. |
| functions | Functions callable from the document's expressions. |
| data | The sample data the design surface is laid out on. |
| page | Page geometry ({ size, margin }), the same values you pass pdf({ page }). |
| fonts | The font mapping, the same record you pass pdf({ fonts }). |
| colorScheme | "light" (default), "dark", or "auto". Chrome only — the pages stay white. |
Events. change fires once per turn in which the document committed — an edit, an undo, a
redo, or several of them in one turn arriving as one event. It carries { schema, problems,
warnings }: the document (deep-frozen), its structured problem list, so you can disable Save
without running the engine yourself, and the engine's advisory warnings. Those warnings are never
fatal, so nothing that gates on problems should gate on them. The two halves always agree, which
means a change needs a plan behind it. While a property the editor rejects or an engine throw is
standing, the editor holds edits rather than delivering them. The next render that reaches the
document then sends the settled result as one event. A render that merely fails still delivers.
The editor checked the document and found it clean, and only the drawing of it went wrong.
error reports host mistakes and render failures, and is your cue that anything derived from the
last change may be stale. A host mistake reports once, not once per render it blocks, and the
panel it opens stays dismissed once dismissed. The panel's label is "The report could not be
displayed." until a render has ever landed, and "The report could not be updated." after.
renderComplete is a Promise<boolean> for the newest render settling: the render that is newest
when it settles, so awaiting it while edits keep landing waits for the last one's render.
Where a node is drawn. boxes(path) answers every rectangle the render drew for a node, in
document order, as client rectangles. Use it to anchor your own UI to a node — a badge, a comment
marker, a tour step. It answers for every page, whether or not that page is on screen. It answers
an empty array when the node draws nothing, when the path is not in the document, and before a
render lands. Await renderComplete first.
Editing. Hover an item for its gutter grip: drag to move (a caret shows exactly where it will
land, and refuses to go anywhere illegal), click for Delete and Duplicate, and the + beside it
inserts a text item. Drag table column headers to reorder — a column's total cell travels with
it. The right rail holds four things: the document panel, the properties panel, the groups list,
and every problem in the document. The document panel edits the report default — the family and
size every band inherits — and stands whether or not anything is selected, because that default
belongs to the document rather than to any band. It also lists the bands off the page:
page.header, page.footer and empty. Nothing on the sheet is ever one of them — a page band is
drawn by a paginating target alone, and empty renders only over a dataset with no rows — so
selecting one there is the only way to reach its items, which the panel then lists with the verbs
that add, remove and reorder them. Dropping into one from a visible band follows the same
anchor rule as any other drop. The properties panel carries real controls for
styles, an fx toggle to make any style value an expression, and errors underlined at the exact
character when the engine knows it.
Mod+Z / Shift+Mod+Z undo and redo whole gestures. Inside a text field they undo typing, not
the document. Escape cancels a drag in progress, else clears the selection. Typing commits as
one gesture once it pauses for half a second, and history keeps the last hundred gestures. While
the document is mid-edit and invalid, the preview keeps the last version that compiled.
Reveal. The render is an honest picture: an empty band or a hidden item draws nothing. The bar's Reveal toggle (off by default) shows placeholders for them — a hidden item's placeholder shows what you wrote in it — and during a drag, empty bands appear as drop targets on their own.
Theming. Chrome styles live on --qe-* custom properties under qe-* class names,
the editor's counterpart of the viewer's --qv-* set. A token set on the element or an ancestor
wins over the pin. The names are a reference seam, not a frozen vocabulary: backdrop, bar, rail
and sheet edge (--qe-backdrop, --qe-bar, --qe-rail, --qe-border, --qe-text,
--qe-sheet-shadow), controls (--qe-field, --qe-icon, --qe-hover, --qe-focus), and the
error panel (--qe-error, --qe-error-text, --qe-error-border), plus --qe-advisory-text for
a warning in the Issues list. The pages stay white.
Documentation
The quario documentation is the reference.
The report schema is the normative
specification of what a report may declare, and
@quario/editor is this element's own API.
License
quario is commercial software. Evaluation is free and fully featured, and quario marks its output
as unlicensed. You state the key once, on the quario() instance you pass in. The editor adds no
license mechanics of its own. See LICENSE.
