Component API

Everything here is exported from @rd/designer. The element class is ReportDesigner, and the tag is report-designer (HTMLElementTagNameMap is augmented, so document.querySelector('report-designer') is typed).

Attributes#

Attributes are strings, so use them in HTML. Each one also has a property of the same meaning.

AttributePropertyTypeDefaultDescription
font-base-urlfontBaseUrlURL string./fonts/ next to the scriptFolder that holds the fonts' manifest.json (@rd/fonts) and, optionally, manifest-cjk.json (@rd/fonts-cjk). Set it whenever a bundler includes the designer (Install)
help-base-urlhelpBaseUrlURL string'' (built-in guide)Optional override. When set, the Help drawer shows the external copy of the user guide at this URL in an iframe (for example the render service's /help/) instead of the built-in one
helphelp"false" / booleantruehelp="false" hides the Help button and disables F1
themetheme'light' | 'dark' | 'auto''light'Color theme (reflected). auto follows the operating system's light or dark mode, live. THEMES lists the values
read-onlyreadOnlybooleanfalseView-only: no edits, palette and editing commands disabled (reflected)
langlangstring'' (browser language)UI language: en, es, fr, de, pt-BR, zh-CN, zh-TW, ja, ko, or any BCP-47 tag (mapped to the nearest)
builtin-gallerybuiltinGallery"false" / booleantruebuiltin-gallery="false" hides the 20 built-in gallery templates

Properties (objects)#

Set these from JavaScript, never as attributes.

PropertyTypeDefaultDescription
templateTemplate | nullnull (blank Letter page)The design. Setting it replaces the design and clears undo history, unless it is deep-equal to the current design (then it is ignored, which makes two-way binding safe). Loading a template never fires template-change
schemaobject | nullnullJSON Schema of your data contract. Drives the Fields tab (Data contracts and JSON Schema)
galleryGalleryEntry[][]Host-supplied gallery entries, listed before the built-ins (Templates and gallery)

Read-only properties:

PropertyTypeDescription
uiLanguagestringThe UI language in effect after resolution and fallback, e.g. 'de'
renderInWorkerbooleantrue when previews render in a Web Worker, false for the main-thread fallback

Note: The element also exposes a store property, the internal editor state. It is not a supported API and may change in any release.

Methods#

MethodReturnsDescription
getTemplate()TemplateA deep copy of the current design
setTemplate(template)voidSame as setting template. null gives a blank Letter page
setSchema(schema)voidSame as setting schema
renderPdf()Promise<RenderResult>Renders the current design in placeholder mode (preview text, no data): { pdf: Uint8Array, pageCount, warnings, license }. Every page carries a light "PREVIEW" watermark: final documents come from the render API (License)
openHelp(topic?, context?)booleanOpens context help. topic is a key of HELP_TOPICS; without it, the topic follows the current selection or open dialog. Fires help-request first. Returns false when help is disabled or the host cancelled the event
closeHelp()voidCloses the help drawer

Events#

All events bubble and are composed, so they cross shadow boundaries.

Eventdetail typeWhen
template-changeTemplateChangeDetail = { template: Template }300 ms after the last user edit (debounced). Not fired when the host loads a template. Toolbar New, Open template JSON and gallery picks count as edits
saveTemplateChangeDetailThe user pressed Ctrl/Cmd+S or clicked Save. A final template-change is dispatched immediately before it
help-requestHelpRequestDetail = { topic: HelpTopic; url: string }Help was requested (Help button, F1 or openHelp()). url is the relative page and anchor in the user guide (07-tables.html#table-settings), or an absolute URL under help-base-url when that override is set. Cancelable: call preventDefault() to show help your own way (Language, help and read-only mode)

Typed listeners:

import type { HelpRequestDetail, ReportDesigner, TemplateChangeDetail } from '@rd/designer';

const designer = document.querySelector('report-designer') as ReportDesigner;

designer.addEventListener('template-change', (e) => {
    const { template } = (e as CustomEvent<TemplateChangeDetail>).detail;
    console.log('edited:', template.name);
});

designer.addEventListener('save', (e) => {
    void saveTemplate('bol', (e as CustomEvent<TemplateChangeDetail>).detail.template);
});

designer.addEventListener('help-request', (e) => {
    const { topic, url } = (e as CustomEvent<HelpRequestDetail>).detail;
    console.log('help for', topic, 'at', url);
});

Slots#

SlotDefaultDescription
logoThe Report Designer iconYour own icon at the left of the toolbar: an <img> or <svg> child with slot="logo", fitted into 26 × 26 px (Styling)
<report-designer font-base-url="/rd-fonts/">
    <img slot="logo" src="/assets/brand-mark.svg" alt="" />
</report-designer>

With mountDesigner, add it to the element it returns:

const logo = Object.assign(document.createElement('img'), { src: '/assets/brand-mark.svg', alt: '' });
logo.slot = 'logo';
handle.element.append(logo);

mountDesigner(container, options)#

Creates a <report-designer> inside container (give the container a height) and returns a handle.

interface MountOptions {
    template?: Template | null;
    schema?: object | null;
    fontBaseUrl?: string;
    helpBaseUrl?: string;          // optional: show an external copy of the guide instead of the built-in one
    help?: boolean;                // false hides help
    theme?: 'light' | 'dark' | 'auto';
    lang?: string;
    gallery?: GalleryEntry[];
    builtinGallery?: boolean;      // false hides the built-in templates
    readOnly?: boolean;
    onChange?: (template: Template) => void;   // debounced 300 ms after edits
    onSave?: (template: Template) => void;     // Ctrl/Cmd+S or Save
}

interface DesignerHandle {
    element: ReportDesigner;
    getTemplate(): Template;
    setTemplate(template: Template | null): void;
    setSchema(schema: object | null): void;
    openHelp(topic?: string): boolean;
    setLang(lang: string): void;   // switches the UI language live
    renderPdf(): Promise<RenderResult>;
    destroy(): void;               // removes the element and its listeners
}

Exported types and values#

ExportKindDescription
ReportDesignerclassThe element class
mountDesigner, MountOptions, DesignerHandlefunction, typesImperative API
TemplateChangeDetail, HelpRequestDetailtypesEvent details
GalleryEntrytypeHost gallery entry (Templates and gallery)
JsonSchematypeThe subset of JSON Schema the Fields tab reads
HELP_TOPICS, HelpTopicconst, typeHelp topic keys mapped to user-guide pages ('element.table' → 07-tables.html#table-settings)
LANGS, LANG_NAMES, resolveLang, Langconst, function, typeSupported UI languages, their native names, and tag resolution (resolveLang('es-MX') → 'es')
createBlankTemplate, validateTemplatefunctionsRe-exported from @rd/core
Template, RenderResult, RenderOptions, ValidationIssue, RenderLicense, …typesAll @rd/core types are re-exported

Building a language picker with the exported values:

import { LANGS, LANG_NAMES, resolveLang, type ReportDesigner } from '@rd/designer';

const designer = document.querySelector('report-designer') as ReportDesigner;
const select = document.createElement('select');
for (const lang of LANGS) {
    select.add(new Option(LANG_NAMES[lang], lang, false, lang === resolveLang(navigator.language)));
}
select.addEventListener('change', () => (designer.lang = select.value));
document.body.prepend(select);

Validating a template before storing it, in the browser or in Node:

import { validateTemplate, type Template } from '@rd/designer';

function assertValid(template: Template): void {
    const errors = validateTemplate(template).filter((i) => i.severity === 'error');
    if (errors.length) {
        throw new Error(errors.map((i) => `${i.path}: ${i.message}`).join('\n'));
    }
}

Keyboard handling and focus#

The element sets tabindex="0" (unless you set one) and handles its shortcuts (Ctrl/Cmd+Z, Ctrl/Cmd+S, Delete, arrows, F1 …) only while focus is inside it, so it doesn't steal shortcuts from the rest of your page. Ctrl/Cmd+S calls preventDefault() so the browser's "Save page" dialog doesn't open.