style: format codebase

This commit is contained in:
hand-dot
2026-05-10 08:06:54 +09:00
parent e3e5a28dfe
commit 9241971e9e
7 changed files with 182 additions and 166 deletions

View File

@@ -349,35 +349,36 @@ const layoutStaticBlocks = async (arg: {
_cache: Map<string | number, unknown>;
}) => {
const contentWidth = Math.max(0, arg.pageSize.width - arg.margin.left - arg.margin.right);
const blockFrames: Array<{ children: PdfJsxChild[]; frame: Rect; placement?: StaticPlacement }> = [
{
children: arg.blocks.staticTop,
frame: { x: 0, y: 0, width: arg.pageSize.width, height: arg.pageSize.height },
},
{
children: arg.blocks.header,
frame: {
x: arg.margin.left,
y: 0,
width: contentWidth,
height: arg.margin.top,
const blockFrames: Array<{ children: PdfJsxChild[]; frame: Rect; placement?: StaticPlacement }> =
[
{
children: arg.blocks.staticTop,
frame: { x: 0, y: 0, width: arg.pageSize.width, height: arg.pageSize.height },
},
},
{
children: arg.blocks.footer,
frame: {
x: arg.margin.left,
y: arg.pageSize.height - arg.margin.bottom,
width: contentWidth,
height: arg.margin.bottom,
{
children: arg.blocks.header,
frame: {
x: arg.margin.left,
y: 0,
width: contentWidth,
height: arg.margin.top,
},
},
},
{
children: arg.blocks.staticBottom,
frame: { x: 0, y: 0, width: arg.pageSize.width, height: arg.pageSize.height },
placement: 'bottom',
},
];
{
children: arg.blocks.footer,
frame: {
x: arg.margin.left,
y: arg.pageSize.height - arg.margin.bottom,
width: contentWidth,
height: arg.margin.bottom,
},
},
{
children: arg.blocks.staticBottom,
frame: { x: 0, y: 0, width: arg.pageSize.width, height: arg.pageSize.height },
placement: 'bottom',
},
];
for (const { children, frame, placement } of blockFrames) {
if (children.length === 0) continue;

View File

@@ -1,6 +1,12 @@
import puppeteer, { Browser, Page } from 'puppeteer';
import { pdf2img } from '@pdfme/converter';
import { Template, Schema, PAGE_SIZE_PRESETS, cloneDeep, getInputFromTemplate } from '@pdfme/common';
import {
Template,
Schema,
PAGE_SIZE_PRESETS,
cloneDeep,
getInputFromTemplate,
} from '@pdfme/common';
import { text, table, image, barcodes, select, checkbox, radioGroup } from '@pdfme/schemas';
import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process';
import { stripVTControlCharacters } from 'node:util';
@@ -240,38 +246,47 @@ async function loadRouteWithStorage(
storageState: PlaygroundStorageState,
) {
const projectId = 'project_e2e_deterministic_template';
const inputs = storageState.inputs ?? (storageState.template ? getInputFromTemplate(storageState.template) : []);
const inputs =
storageState.inputs ??
(storageState.template ? getInputFromTemplate(storageState.template) : []);
await page.goto(baseUrl, { waitUntil: 'networkidle2', timeout });
await page.evaluate((state, resolvedInputs, id, projectsStorageKey, activeProjectStorageKey) => {
localStorage.removeItem('template');
localStorage.removeItem('inputs');
localStorage.removeItem('mode');
localStorage.removeItem(projectsStorageKey);
localStorage.removeItem(activeProjectStorageKey);
await page.evaluate(
(state, resolvedInputs, id, projectsStorageKey, activeProjectStorageKey) => {
localStorage.removeItem('template');
localStorage.removeItem('inputs');
localStorage.removeItem('mode');
localStorage.removeItem(projectsStorageKey);
localStorage.removeItem(activeProjectStorageKey);
if (state.template) {
const now = Date.now();
localStorage.setItem(
projectsStorageKey,
JSON.stringify([
{
createdAt: now,
id,
inputs: resolvedInputs,
kind: 'template',
template: state.template,
title: 'E2E deterministic template',
updatedAt: now,
},
]),
);
localStorage.setItem(activeProjectStorageKey, id);
}
if (state.mode) {
localStorage.setItem('mode', state.mode);
}
}, storageState, inputs, projectId, playgroundProjectsStorageKey, activePlaygroundProjectStorageKey);
if (state.template) {
const now = Date.now();
localStorage.setItem(
projectsStorageKey,
JSON.stringify([
{
createdAt: now,
id,
inputs: resolvedInputs,
kind: 'template',
template: state.template,
title: 'E2E deterministic template',
updatedAt: now,
},
]),
);
localStorage.setItem(activeProjectStorageKey, id);
}
if (state.mode) {
localStorage.setItem('mode', state.mode);
}
},
storageState,
inputs,
projectId,
playgroundProjectsStorageKey,
activePlaygroundProjectStorageKey,
);
await page.goto(`${baseUrl}${path}?project=${encodeURIComponent(projectId)}`, {
waitUntil: 'networkidle2',
@@ -492,7 +507,10 @@ describe('Playground E2E Tests', () => {
if (!browser || !page) throw new Error('Browser/Page not initialized');
// 5. Load the Pedigree designer directly to avoid flaky list-page navigation in CI
await page.goto(`${baseUrl}/designer?template=pedigree`, { waitUntil: 'networkidle2', timeout });
await page.goto(`${baseUrl}/designer?template=pedigree`, {
waitUntil: 'networkidle2',
timeout,
});
await waitForDesignerReady(page, 'Pet Name');

View File

@@ -18,29 +18,25 @@ const buttonVariants: Record<PlaygroundButtonVariant, string> = {
const joinClassNames = (...classes: Array<string | false | null | undefined>) =>
classes.filter(Boolean).join(' ');
const PlaygroundButton = forwardRef<HTMLButtonElement, PlaygroundButtonProps>(function PlaygroundButton(
{
className,
fullWidth = false,
type = 'button',
variant = 'secondary',
...props
const PlaygroundButton = forwardRef<HTMLButtonElement, PlaygroundButtonProps>(
function PlaygroundButton(
{ className, fullWidth = false, type = 'button', variant = 'secondary', ...props },
ref,
) {
return (
<button
ref={ref}
type={type}
className={joinClassNames(
'inline-flex min-w-0 items-center justify-center gap-1 whitespace-nowrap rounded border px-2 py-1.5 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:px-3',
buttonVariants[variant],
fullWidth && 'w-full',
className,
)}
{...props}
/>
);
},
ref,
) {
return (
<button
ref={ref}
type={type}
className={joinClassNames(
'inline-flex min-w-0 items-center justify-center gap-1 whitespace-nowrap rounded border px-2 py-1.5 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-offset-2 sm:px-3',
buttonVariants[variant],
fullWidth && 'w-full',
className,
)}
{...props}
/>
);
});
);
export default PlaygroundButton;

View File

@@ -124,8 +124,8 @@ function DesignerApp() {
const currentTitle =
(currentProject?.title ?? projectTitleRef.current) || 'Untitled Template';
const title = saveAs
? window.prompt('Save as', `${currentTitle} Copy`) ?? ''
: currentProject?.title ?? window.prompt('Project name', currentTitle) ?? '';
? (window.prompt('Save as', `${currentTitle} Copy`) ?? '')
: (currentProject?.title ?? window.prompt('Project name', currentTitle) ?? '');
if (!title.trim()) return;
const thumbnail = await createTemplateThumbnailDataUrl(
@@ -148,86 +148,89 @@ function DesignerApp() {
[setCurrentProjectTitle],
);
const buildDesigner = useCallback(async (isCancelled: () => boolean) => {
if (!designerRef.current) return;
try {
let template: Template = getBlankTemplate();
let project: PlaygroundProject | null = null;
loadRequestRef.current ??= getDesignerLoadRequest();
const {
projectId: projectIdFromQuery,
searchParams: initialSearchParams,
shouldConsumeQuery,
shouldCreateNewProject,
templateId: templateIdFromQuery,
} = loadRequestRef.current;
const buildDesigner = useCallback(
async (isCancelled: () => boolean) => {
if (!designerRef.current) return;
try {
let template: Template = getBlankTemplate();
let project: PlaygroundProject | null = null;
loadRequestRef.current ??= getDesignerLoadRequest();
const {
projectId: projectIdFromQuery,
searchParams: initialSearchParams,
shouldConsumeQuery,
shouldCreateNewProject,
templateId: templateIdFromQuery,
} = loadRequestRef.current;
if (shouldCreateNewProject) {
clearActivePlaygroundProject();
setCurrentProjectTitle('Untitled Template');
} else if (projectIdFromQuery) {
project = getPlaygroundProject(projectIdFromQuery);
if (!project) throw new Error('Project not found');
template = project.template;
} else if (templateIdFromQuery) {
const templateJson = await getTemplateById(templateIdFromQuery);
checkTemplate(templateJson);
template = templateJson;
setCurrentProjectTitle(fromKebabCase(templateIdFromQuery));
} else {
project = getActivePlaygroundProject();
if (project) {
if (shouldCreateNewProject) {
clearActivePlaygroundProject();
setCurrentProjectTitle('Untitled Template');
} else if (projectIdFromQuery) {
project = getPlaygroundProject(projectIdFromQuery);
if (!project) throw new Error('Project not found');
template = project.template;
} else if (templateIdFromQuery) {
const templateJson = await getTemplateById(templateIdFromQuery);
checkTemplate(templateJson);
template = templateJson;
setCurrentProjectTitle(fromKebabCase(templateIdFromQuery));
} else {
template = await getDefaultPlaygroundTemplate();
setCurrentProjectTitle(fromKebabCase('invoice'));
project = getActivePlaygroundProject();
if (project) {
template = project.template;
} else {
template = await getDefaultPlaygroundTemplate();
setCurrentProjectTitle(fromKebabCase('invoice'));
}
}
projectRef.current = project;
if (project) setCurrentProjectTitle(project.title);
if (shouldConsumeQuery && !didCleanLoadQueryRef.current) {
const nextSearchParams = new URLSearchParams(initialSearchParams);
nextSearchParams.delete('new');
nextSearchParams.delete('template');
nextSearchParams.delete('project');
didCleanLoadQueryRef.current = true;
setSearchParams(nextSearchParams, { replace: true });
}
if (isCancelled() || !designerRef.current) return null;
const nextDesigner = new Designer({
domContainer: designerRef.current,
template,
options: {
font: getFontsData(),
lang: 'en',
labels: {
'signature.clear': '🗑️',
},
theme: {
token: { colorPrimary: '#25c2a0' },
},
icons: {
multiVariableText:
'<svg fill="#000000" width="24px" height="24px" viewBox="0 0 24 24"><path d="M6.643,13.072,17.414,2.3a1.027,1.027,0,0,1,1.452,0L20.7,4.134a1.027,1.027,0,0,1,0,1.452L9.928,16.357,5,18ZM21,20H3a1,1,0,0,0,0,2H21a1,1,0,0,0,0-2Z"/></svg>',
},
maxZoom: 250,
},
plugins: getPlugins(),
});
designer.current = nextDesigner;
nextDesigner.onSaveTemplate(onSaveTemplate);
return nextDesigner;
} catch (error) {
if (isCancelled()) return null;
projectRef.current = null;
console.error(error);
return null;
}
projectRef.current = project;
if (project) setCurrentProjectTitle(project.title);
if (shouldConsumeQuery && !didCleanLoadQueryRef.current) {
const nextSearchParams = new URLSearchParams(initialSearchParams);
nextSearchParams.delete('new');
nextSearchParams.delete('template');
nextSearchParams.delete('project');
didCleanLoadQueryRef.current = true;
setSearchParams(nextSearchParams, { replace: true });
}
if (isCancelled() || !designerRef.current) return null;
const nextDesigner = new Designer({
domContainer: designerRef.current,
template,
options: {
font: getFontsData(),
lang: 'en',
labels: {
'signature.clear': '🗑️',
},
theme: {
token: { colorPrimary: '#25c2a0' },
},
icons: {
multiVariableText:
'<svg fill="#000000" width="24px" height="24px" viewBox="0 0 24 24"><path d="M6.643,13.072,17.414,2.3a1.027,1.027,0,0,1,1.452,0L20.7,4.134a1.027,1.027,0,0,1,0,1.452L9.928,16.357,5,18ZM21,20H3a1,1,0,0,0,0,2H21a1,1,0,0,0,0-2Z"/></svg>',
},
maxZoom: 250,
},
plugins: getPlugins(),
});
designer.current = nextDesigner;
nextDesigner.onSaveTemplate(onSaveTemplate);
return nextDesigner;
} catch (error) {
if (isCancelled()) return null;
projectRef.current = null;
console.error(error);
return null;
}
}, [onSaveTemplate, setCurrentProjectTitle, setSearchParams]);
},
[onSaveTemplate, setCurrentProjectTitle, setSearchParams],
);
const onChangeBasePDF = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files?.[0]) {

View File

@@ -168,8 +168,8 @@ function FormAndViewerApp() {
const nextTemplate = ui.current.getTemplate();
const currentTitle = (currentProject?.title ?? projectTitle) || 'Untitled Template';
const title = saveAs
? window.prompt('Save as', `${currentTitle} Copy`) ?? ''
: currentProject?.title ?? window.prompt('Project name', currentTitle) ?? '';
? (window.prompt('Save as', `${currentTitle} Copy`) ?? '')
: (currentProject?.title ?? window.prompt('Project name', currentTitle) ?? '');
if (!title.trim()) return;
const thumbnail = await createTemplateThumbnailDataUrl(nextTemplate, nextInputs).catch(
@@ -248,9 +248,7 @@ function FormAndViewerApp() {
<div className="flex gap-1">
<PlaygroundButton onClick={onGetInputs}>Get</PlaygroundButton>
<PlaygroundButton onClick={onSetInputs}>Set</PlaygroundButton>
<PlaygroundButton onClick={() => void onSaveInputs()}>
Save
</PlaygroundButton>
<PlaygroundButton onClick={() => void onSaveInputs()}>Save</PlaygroundButton>
<PlaygroundButton onClick={() => void onSaveInputs(true)}>Save As</PlaygroundButton>
<PlaygroundButton onClick={onResetInputs}>Reset</PlaygroundButton>
</div>

View File

@@ -342,7 +342,7 @@ export default function JsxPlayground() {
title ??
(saveAs
? window.prompt('Save as', `${currentTitle} Copy`)
: projectRef.current?.title ?? window.prompt('Project name', currentTitle)) ??
: (projectRef.current?.title ?? window.prompt('Project name', currentTitle))) ??
'';
if (!projectTitle.trim()) return null;

View File

@@ -178,8 +178,8 @@ export default function Md2Pdf() {
const currentTitle = projectRef.current?.title ?? `md2pdf - ${sourceTitle}`;
const title = saveAs
? window.prompt('Save as', `${currentTitle} Copy`) ?? ''
: projectRef.current?.title ?? window.prompt('Project name', currentTitle) ?? '';
? (window.prompt('Save as', `${currentTitle} Copy`) ?? '')
: (projectRef.current?.title ?? window.prompt('Project name', currentTitle) ?? '');
if (!title.trim()) return;
try {