fixed layout canvas including elements
This commit is contained in:
@@ -45,7 +45,7 @@ import {
|
||||
normalizeElements,
|
||||
payloadToElements,
|
||||
} from './invite-layout/schema';
|
||||
import { CanvasScaleControl, DesignerCanvas } from './invite-layout/DesignerCanvas';
|
||||
import { DesignerCanvas } from './invite-layout/DesignerCanvas';
|
||||
import { CANVAS_HEIGHT, CANVAS_WIDTH } from './invite-layout/schema';
|
||||
import {
|
||||
generatePdfBytes,
|
||||
@@ -181,9 +181,6 @@ type InviteLayoutCustomizerPanelProps = {
|
||||
};
|
||||
|
||||
const MAX_INSTRUCTIONS = 5;
|
||||
const MIN_CANVAS_SCALE = 0.15;
|
||||
const MAX_CANVAS_SCALE = 0.85;
|
||||
const SCALE_EPSILON = 0.005;
|
||||
|
||||
export function InviteLayoutCustomizerPanel({
|
||||
invite,
|
||||
@@ -215,8 +212,6 @@ export function InviteLayoutCustomizerPanel({
|
||||
const [printBusy, setPrintBusy] = React.useState(false);
|
||||
const [elements, setElements] = React.useState<LayoutElement[]>([]);
|
||||
const [activeElementId, setActiveElementId] = React.useState<string | null>(null);
|
||||
const [canvasScale, setCanvasScale] = React.useState(0.45);
|
||||
const [autoScaleEnabled, setAutoScaleEnabled] = React.useState(true);
|
||||
const [showFloatingActions, setShowFloatingActions] = React.useState(false);
|
||||
const actionsSentinelRef = React.useRef<HTMLDivElement | null>(null);
|
||||
const historyRef = React.useRef<LayoutElement[][]>([]);
|
||||
@@ -345,26 +340,6 @@ export function InviteLayoutCustomizerPanel({
|
||||
const prevFormRef = React.useRef(form);
|
||||
const initializedLayoutsRef = React.useRef<Record<string, boolean>>({});
|
||||
const prevInviteRef = React.useRef<number | string | null>(null);
|
||||
const clampScale = React.useCallback(
|
||||
(value: number) => Math.min(MAX_CANVAS_SCALE, Math.max(MIN_CANVAS_SCALE, value)),
|
||||
[],
|
||||
);
|
||||
|
||||
const updateAutoScale = React.useCallback(() => {
|
||||
if (!autoScaleEnabled) {
|
||||
return;
|
||||
}
|
||||
const viewport = designerViewportRef.current;
|
||||
const availableWidth = viewport?.clientWidth ?? 0;
|
||||
const widthScale = availableWidth > 0 ? availableWidth / CANVAS_WIDTH : NaN;
|
||||
const heightBudget = Math.max(window.innerHeight * 0.75 - 48, 200);
|
||||
const heightScale = heightBudget / CANVAS_HEIGHT;
|
||||
const resolvedWidthScale = Number.isFinite(widthScale) ? widthScale : Number.POSITIVE_INFINITY;
|
||||
const candidate = Math.min(resolvedWidthScale, heightScale, MAX_CANVAS_SCALE);
|
||||
const targetScale = clampScale(Number.isFinite(candidate) ? candidate : canvasScale);
|
||||
setCanvasScale((prev) => (Math.abs(prev - targetScale) > SCALE_EPSILON ? targetScale : prev));
|
||||
}, [autoScaleEnabled, canvasScale, clampScale]);
|
||||
|
||||
const activeLayout = React.useMemo(() => {
|
||||
if (!availableLayouts.length) {
|
||||
return null;
|
||||
@@ -431,9 +406,7 @@ export function InviteLayoutCustomizerPanel({
|
||||
resetHistory(defaults);
|
||||
setActiveElementId(null);
|
||||
initializedLayoutsRef.current[activeLayout.id] = true;
|
||||
setAutoScaleEnabled(true);
|
||||
updateAutoScale();
|
||||
}, [activeLayout, eventName, activeLayoutQrSize, commitElements, elements, elementsAreEqual, resetHistory, updateAutoScale]);
|
||||
}, [activeLayout, eventName, activeLayoutQrSize, commitElements, elements, elementsAreEqual, resetHistory]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!invite) {
|
||||
@@ -454,7 +427,6 @@ export function InviteLayoutCustomizerPanel({
|
||||
}
|
||||
return layouts[0]?.id;
|
||||
});
|
||||
setAutoScaleEnabled(true);
|
||||
}, [invite?.id, initialCustomization?.layout_id]);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -541,27 +513,6 @@ export function InviteLayoutCustomizerPanel({
|
||||
});
|
||||
}, [availableLayouts, initialCustomization?.layout_id]);
|
||||
|
||||
React.useEffect(() => {
|
||||
updateAutoScale();
|
||||
}, [updateAutoScale]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setAutoScaleEnabled(true);
|
||||
updateAutoScale();
|
||||
}, [activeLayout?.id, updateAutoScale]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const viewport = designerViewportRef.current;
|
||||
if (!viewport || typeof ResizeObserver === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(() => updateAutoScale());
|
||||
observer.observe(viewport);
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [updateAutoScale]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!invite || !activeLayout) {
|
||||
setForm({});
|
||||
@@ -1751,36 +1702,27 @@ export function InviteLayoutCustomizerPanel({
|
||||
<div ref={actionsSentinelRef} className="h-1 w-full" />
|
||||
</form>
|
||||
<div className="flex flex-col gap-4 rounded-2xl border border-[var(--tenant-border-strong)] bg-[var(--tenant-surface)] p-5 shadow-sm transition-colors">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<CanvasScaleControl
|
||||
scale={canvasScale}
|
||||
onChange={(value) => {
|
||||
setAutoScaleEnabled(false);
|
||||
setCanvasScale(clampScale(value));
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleUndo}
|
||||
disabled={!canUndo}
|
||||
>
|
||||
<Undo2 className="mr-1 h-4 w-4" />
|
||||
{t('invites.customizer.actions.undo', 'Rückgängig')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleRedo}
|
||||
disabled={!canRedo}
|
||||
>
|
||||
<Redo2 className="mr-1 h-4 w-4" />
|
||||
{t('invites.customizer.actions.redo', 'Wiederholen')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center justify-end gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleUndo}
|
||||
disabled={!canUndo}
|
||||
>
|
||||
<Undo2 className="mr-1 h-4 w-4" />
|
||||
{t('invites.customizer.actions.undo', 'Rückgängig')}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleRedo}
|
||||
disabled={!canRedo}
|
||||
>
|
||||
<Redo2 className="mr-1 h-4 w-4" />
|
||||
{t('invites.customizer.actions.redo', 'Wiederholen')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
@@ -1802,7 +1744,6 @@ export function InviteLayoutCustomizerPanel({
|
||||
badge={form.badge_color ?? form.accent_color ?? '#2563EB'}
|
||||
qrCodeDataUrl={qrCodeDataUrl}
|
||||
logoDataUrl={form.logo_data_url ?? form.logo_url ?? null}
|
||||
scale={canvasScale}
|
||||
layoutKey={`designer:${invite?.id ?? 'unknown'}:${activeLayout?.id ?? 'default'}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user