mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-07 15:25:47 +00:00
feat(certificate-designer): add custom page size options and update related components
This commit is contained in:
@@ -23,6 +23,10 @@ interface Props {
|
||||
onLogoPlacementChange: (placement: TemplateLogoPlacement) => void;
|
||||
landscape: boolean;
|
||||
onLandscapeChange: (landscape: boolean) => void;
|
||||
pageWidth: string;
|
||||
onPageWidthChange: (width: string) => void;
|
||||
pageHeight: string;
|
||||
onPageHeightChange: (height: string) => void;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
@@ -51,6 +55,10 @@ export function TemplateBackgroundPanel({
|
||||
onLogoPlacementChange,
|
||||
landscape,
|
||||
onLandscapeChange,
|
||||
pageWidth,
|
||||
onPageWidthChange,
|
||||
pageHeight,
|
||||
onPageHeightChange,
|
||||
disabled,
|
||||
}: Props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -91,9 +99,40 @@ export function TemplateBackgroundPanel({
|
||||
]}
|
||||
/>
|
||||
<Text size="xs" c="dimmed" mt={4}>
|
||||
{landscape
|
||||
? t('designer.a4Landscape', 'A4 — 297 × 210 mm')
|
||||
: t('designer.a4Portrait', 'A4 — 210 × 297 mm')}
|
||||
{pageWidth && pageHeight
|
||||
? t('designer.customSize', 'Custom size — see below')
|
||||
: landscape
|
||||
? t('designer.a4Landscape', 'A4 — 297 × 210 mm')
|
||||
: t('designer.a4Portrait', 'A4 — 210 × 297 mm')}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Text size="sm" fw={500} mb={4}>
|
||||
{t('designer.customPageSize', 'Custom page size')}
|
||||
</Text>
|
||||
<Group gap="xs">
|
||||
<TextInput
|
||||
placeholder={t('designer.pageWidth', 'Width')}
|
||||
value={pageWidth}
|
||||
onChange={(e) => onPageWidthChange(e.currentTarget.value)}
|
||||
disabled={disabled}
|
||||
w={100}
|
||||
/>
|
||||
<Text size="sm" c="dimmed">×</Text>
|
||||
<TextInput
|
||||
placeholder={t('designer.pageHeight', 'Height')}
|
||||
value={pageHeight}
|
||||
onChange={(e) => onPageHeightChange(e.currentTarget.value)}
|
||||
disabled={disabled}
|
||||
w={100}
|
||||
/>
|
||||
</Group>
|
||||
<Text size="xs" c="dimmed" mt={4}>
|
||||
{t(
|
||||
'designer.customSizeHint',
|
||||
'e.g. 4.92in × 3.46in. Leave both blank to use A4. Overrides orientation\'s A4 size when set.',
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
|
||||
@@ -8,6 +8,8 @@ interface Props {
|
||||
logoUrl: string;
|
||||
logoPlacement: TemplateLogoPlacement;
|
||||
landscape: boolean;
|
||||
pageWidth?: string;
|
||||
pageHeight?: string;
|
||||
placements: TemplateFieldPlacement[];
|
||||
selectedId: string | null;
|
||||
onSelect: (id: string | null) => void;
|
||||
@@ -15,9 +17,17 @@ interface Props {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
/** A4 aspect ratio, the only page size the renderer is configured for. */
|
||||
/** A4 aspect ratio, the fallback for a version with no custom page size. */
|
||||
const A4_RATIO = 297 / 210;
|
||||
|
||||
/** Parses a CSS length like "4.92in" or "125mm" into a unitless number, unit-agnostic — only the ratio between width and height matters here. */
|
||||
function parseLength(value: string): number | null {
|
||||
const match = value.trim().match(/^([\d.]+)/);
|
||||
if (!match) return null;
|
||||
const n = Number(match[1]);
|
||||
return Number.isFinite(n) && n > 0 ? n : null;
|
||||
}
|
||||
|
||||
const LOGO_CORNER_STYLE: Record<string, (offset: number) => React.CSSProperties> = {
|
||||
TOP_LEFT: (o) => ({ top: `${o}%`, left: `${o}%` }),
|
||||
TOP_CENTER: (o) => ({ top: `${o}%`, left: '50%', transform: 'translateX(-50%)' }),
|
||||
@@ -53,6 +63,8 @@ export function TemplateCanvas({
|
||||
logoUrl,
|
||||
logoPlacement,
|
||||
landscape,
|
||||
pageWidth,
|
||||
pageHeight,
|
||||
placements,
|
||||
selectedId,
|
||||
onSelect,
|
||||
@@ -180,6 +192,18 @@ export function TemplateCanvas({
|
||||
logoPlacement.offsetPct ?? 5,
|
||||
);
|
||||
|
||||
// A custom size already states its own orientation (4.92in × 3.46in is
|
||||
// landscape on its own), so it is used as-is rather than flipped again by
|
||||
// `landscape` — that flag only disambiguates the A4 fallback below.
|
||||
const customWidth = pageWidth ? parseLength(pageWidth) : null;
|
||||
const customHeight = pageHeight ? parseLength(pageHeight) : null;
|
||||
const aspectRatio =
|
||||
customWidth && customHeight
|
||||
? customWidth / customHeight
|
||||
: landscape
|
||||
? A4_RATIO
|
||||
: 1 / A4_RATIO;
|
||||
|
||||
return (
|
||||
<Paper withBorder p="sm" radius="md">
|
||||
<Text size="xs" c="dimmed" mb="xs">
|
||||
@@ -195,7 +219,7 @@ export function TemplateCanvas({
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
aspectRatio: landscape ? String(A4_RATIO) : String(1 / A4_RATIO),
|
||||
aspectRatio: String(aspectRatio),
|
||||
background: '#ffffff',
|
||||
border: '1px solid var(--mantine-color-gray-4)',
|
||||
overflow: 'hidden',
|
||||
|
||||
Reference in New Issue
Block a user