@pdfme/ui
Version:
TypeScript base PDF generator and React base UI. Open source, developed by the community, and completely free to use under the MIT license!
180 lines (144 loc) • 4.1 kB
text/typescript
import ReactDOM from 'react-dom';
import { DESTROYED_ERR_MSG, DEFAULT_LANG } from './constants.js';
import { debounce } from './helper.js';
import {
cloneDeep,
Template,
Size,
Lang,
Font,
UIProps,
UIOptions,
PluginRegistry,
PreviewProps,
getDefaultFont,
checkUIProps,
checkTemplate,
checkInputs,
checkUIOptions,
checkPreviewProps,
pluginRegistry,
} from '@pdfme/common';
import { builtInPlugins } from '@pdfme/schemas';
export abstract class BaseUIClass {
protected domContainer!: HTMLElement | null;
protected template!: Template;
protected size!: Size;
private lang: Lang = DEFAULT_LANG;
private font: Font = getDefaultFont();
private pluginsRegistry: PluginRegistry = pluginRegistry(builtInPlugins);
private options: UIOptions = {};
private readonly setSize = debounce(() => {
if (!this.domContainer) {
return;
}
this.size = {
height: this.domContainer.clientHeight || window.innerHeight,
width: this.domContainer.clientWidth || window.innerWidth,
};
this.render();
}, 100);
resizeObserver = new ResizeObserver(this.setSize);
constructor(props: UIProps) {
checkUIProps(props);
const { domContainer, template, options = {}, plugins = {} } = props;
this.domContainer = domContainer;
this.template = cloneDeep(template);
this.options = options;
this.size = {
height: this.domContainer.clientHeight || window.innerHeight,
width: this.domContainer.clientWidth || window.innerWidth,
};
this.resizeObserver.observe(this.domContainer);
const { lang, font } = options;
if (lang) {
this.lang = lang;
}
if (font) {
this.font = font;
}
if (Object.values(plugins).length > 0) {
this.pluginsRegistry = pluginRegistry(plugins);
}
}
protected getLang() {
return this.lang;
}
protected getFont() {
return this.font;
}
protected getPluginsRegistry() {
return this.pluginsRegistry;
}
public getOptions() {
return this.options;
}
public getTemplate() {
if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
return this.template;
}
public updateTemplate(template: Template) {
checkTemplate(template);
if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
this.template = cloneDeep(template);
this.render();
}
public updateOptions(options: UIOptions) {
checkUIOptions(options);
const { lang, font } = options || {};
if (lang) {
this.lang = lang;
}
if (font) {
this.font = font;
}
this.options = Object.assign(this.options, options);
this.render();
}
public destroy() {
if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
ReactDOM.unmountComponentAtNode(this.domContainer);
this.resizeObserver.unobserve(this.domContainer);
this.domContainer = null;
}
protected abstract render(): void;
}
export abstract class PreviewUI extends BaseUIClass {
protected inputs!: { [key: string]: string }[];
constructor(props: PreviewProps) {
super(props);
checkPreviewProps(props);
this.inputs = convertToStingObjectArray(cloneDeep(props.inputs));
}
public getInputs() {
if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
return this.inputs;
}
public setInputs(inputs: { [key: string]: string }[]) {
if (!this.domContainer) throw Error(DESTROYED_ERR_MSG);
checkInputs(inputs);
this.inputs = convertToStingObjectArray(inputs);
this.render();
}
protected abstract render(): void;
}
type DataItem = {
[key: string]: string | string[][];
};
type StringifiedDataItem = {
[key: string]: string;
};
function convertToStingObjectArray(data: DataItem[]): StringifiedDataItem[] {
return data.map((item) => {
const stringifiedItem: StringifiedDataItem = {};
Object.keys(item).forEach((key) => {
const value = item[key];
if (Array.isArray(value)) {
stringifiedItem[key] = JSON.stringify(value);
} else {
stringifiedItem[key] = value;
}
});
return stringifiedItem;
});
}