using pressables to change numerical values

This commit is contained in:
Codex Agent
2025-12-14 18:24:47 +01:00
parent a35b56f09d
commit c8b149d887

View File

@@ -306,6 +306,7 @@ type SlotDefinition = {
fontFamily?: string;
lineHeight?: number;
align?: 'left' | 'center' | 'right';
color?: string;
};
type SlotOverrides = Record<string, Partial<SlotDefinition>>;
@@ -345,6 +346,7 @@ function normalizeSlotOverrides(raw: unknown): SlotOverrides {
fontSize: toNumber(slot.fontSize),
lineHeight: toNumber(slot.lineHeight),
fontFamily: typeof slot.fontFamily === 'string' ? slot.fontFamily : undefined,
color: typeof slot.color === 'string' ? slot.color : undefined,
align,
};
return acc;
@@ -392,11 +394,11 @@ function renderEventName(name: TenantEvent['name'] | null | undefined): string |
function getDefaultSlots(): Record<string, SlotDefinition> {
return {
headline: { x: 0.08, y: 0.08, w: 0.84, fontSize: 30, fontWeight: 800, fontFamily: 'Playfair Display', align: 'center' },
subtitle: { x: 0.1, y: 0.16, w: 0.8, fontSize: 18, fontWeight: 600, fontFamily: 'Montserrat', align: 'center' },
description: { x: 0.1, y: 0.22, w: 0.8, fontSize: 16, fontFamily: 'Lora', lineHeight: 1.4, align: 'center' },
headline: { x: 0.08, y: 0.15, w: 0.84, fontSize: 50, fontWeight: 800, fontFamily: 'Playfair Display', align: 'center' },
subtitle: { x: 0.1, y: 0.21, w: 0.8, fontSize: 37, fontWeight: 600, fontFamily: 'Montserrat', align: 'center' },
description: { x: 0.1, y: 0.66, w: 0.8, fontSize: 13, fontFamily: 'Lora', lineHeight: 1.4, align: 'center' },
instructions: { x: 0.1, y: 0.34, w: 0.8, fontSize: 14, fontFamily: 'Lora', lineHeight: 1.35 },
qr: { x: 0.35, y: 0.55, w: 0.3 },
qr: { x: 0.3, y: 0.3, w: 0.28 },
};
}
@@ -425,11 +427,11 @@ function resolveSlots(layout: EventQrInviteLayout | null, isFoldable: boolean, o
const baseSlots = isFoldable
? {
headline: { x: 0.1, y: 0.1, w: 0.8, fontSize: 28, fontWeight: 800, fontFamily: 'Playfair Display', align: 'center' },
subtitle: { x: 0.12, y: 0.18, w: 0.76, fontSize: 18, fontWeight: 600, fontFamily: 'Montserrat', align: 'center' },
description: { x: 0.12, y: 0.24, w: 0.76, fontSize: 15, fontFamily: 'Lora', lineHeight: 1.4, align: 'center' },
headline: { x: 0.08, y: 0.15, w: 0.84, fontSize: 50, fontWeight: 800, fontFamily: 'Playfair Display', align: 'center' },
subtitle: { x: 0.1, y: 0.21, w: 0.8, fontSize: 37, fontWeight: 600, fontFamily: 'Montserrat', align: 'center' },
description: { x: 0.1, y: 0.66, w: 0.8, fontSize: 13, fontFamily: 'Lora', lineHeight: 1.4, align: 'center' },
instructions: { x: 0.12, y: 0.36, w: 0.76, fontSize: 13, fontFamily: 'Lora', lineHeight: 1.3 },
qr: { x: 0.36, y: 0.56, w: 0.28 },
qr: { x: 0.3, y: 0.3, w: 0.28 },
}
: getDefaultSlots();
@@ -452,6 +454,7 @@ function applySlotOverrides(baseSlots: Record<string, SlotDefinition>, overrides
fontFamily: override.fontFamily ?? base.fontFamily,
lineHeight: override.lineHeight ?? base.lineHeight,
align: override.align ?? base.align,
color: override.color ?? base.color,
};
return acc;
}, {});
@@ -544,7 +547,7 @@ function buildFabricOptions({
align: slot.align ?? 'center',
lineHeight: slot.lineHeight ?? 1.35,
content,
fill: textColor,
fill: slot.color ?? textColor,
fontFamily: slot.fontFamily,
rotation,
});
@@ -652,7 +655,11 @@ function BackgroundStep({
null;
const isFoldable = (resolvedLayout?.panel_mode ?? '').toLowerCase() === 'double-mirror';
const formatLabel = isFoldable ? 'A4 Landscape (Double/Mirror)' : 'A4 Portrait';
const formatPaper = resolvePaper(resolvedLayout).toUpperCase();
const orientation = ((resolvedLayout?.orientation ?? (isFoldable ? 'landscape' : 'portrait')) || '').toLowerCase() === 'landscape' ? 'Landscape' : 'Portrait';
const formatLabel = isFoldable
? `${formatPaper} Landscape (double A5/mirrored)`
: `${formatPaper} ${orientation}`;
const disablePresets = isFoldable;
const gradientPresets = [
{ angle: 180, stops: ['#F8FAFC', '#EEF2FF', '#F8FAFC'], label: 'Soft Lilac' },
@@ -1129,12 +1136,13 @@ function LayoutControls({
}, [tenantFonts]);
const numberInputStyle: React.CSSProperties = {
width: '100%',
width: 90,
padding: '10px 12px',
border: '1px solid #e5e7eb',
borderRadius: 10,
fontSize: 14,
background: '#fff',
appearance: 'textfield',
};
const selectStyle: React.CSSProperties = {
@@ -1146,6 +1154,50 @@ function LayoutControls({
background: '#fff',
};
const StepperInput = ({
value,
min,
max,
step,
onChange,
}: {
value: number;
min: number;
max: number;
step: number;
onChange: (value: number) => void;
}) => {
const dec = () => onChange(clampValue(value - step, min, max));
const inc = () => onChange(clampValue(value + step, min, max));
return (
<XStack space="$1" alignItems="center">
<Pressable onPress={dec}>
<XStack width={36} height={36} borderRadius={10} alignItems="center" justifyContent="center" borderWidth={1} borderColor="#e5e7eb" backgroundColor="#fff">
<Text fontSize="$md" fontWeight="800" color="#111827">
</Text>
</XStack>
</Pressable>
<input
type="text"
inputMode="decimal"
min={min}
max={max}
value={value.toFixed(step < 1 ? 1 : 0)}
onChange={(event) => onChange(Number(event.target.value))}
style={numberInputStyle}
/>
<Pressable onPress={inc}>
<XStack width={36} height={36} borderRadius={10} alignItems="center" justifyContent="center" borderWidth={1} borderColor="#e5e7eb" backgroundColor="#fff">
<Text fontSize="$md" fontWeight="800" color="#111827">
+
</Text>
</XStack>
</Pressable>
</XStack>
);
};
const renderTextSlot = (slotKey: string, label: string) => {
const slot = slots[slotKey];
if (!slot) return null;
@@ -1175,80 +1227,116 @@ function LayoutControls({
<Text fontSize="$sm" fontWeight="700" color="#111827">
{label}
</Text>
<XStack space="$2">
<XStack space="$3">
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
X (%)
</Text>
<input
type="number"
step="0.5"
min="0"
max="100"
value={((override.x ?? slot.x) * 100).toFixed(1)}
onChange={(event) => onPercentChange('x')(Number(event.target.value))}
style={numberInputStyle}
/>
<XStack space="$1" alignItems="center">
<Pressable onPress={() => onPercentChange('x')(((override.x ?? slot.x) * 100) - 0.5)}>
<XStack width={36} height={36} borderRadius={10} alignItems="center" justifyContent="center" borderWidth={1} borderColor="#e5e7eb" backgroundColor="#fff">
<Text fontSize="$md" fontWeight="800" color="#111827">
</Text>
</XStack>
</Pressable>
<input
type="text"
inputMode="decimal"
step="0.5"
min="0"
max="100"
value={((override.x ?? slot.x) * 100).toFixed(1)}
onChange={(event) => onPercentChange('x')(Number(event.target.value))}
style={numberInputStyle}
/>
<Pressable onPress={() => onPercentChange('x')(((override.x ?? slot.x) * 100) + 0.5)}>
<XStack width={36} height={36} borderRadius={10} alignItems="center" justifyContent="center" borderWidth={1} borderColor="#e5e7eb" backgroundColor="#fff">
<Text fontSize="$md" fontWeight="800" color="#111827">
+
</Text>
</XStack>
</Pressable>
</XStack>
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
Y (%)
</Text>
<input
type="number"
step="0.5"
min="0"
max="100"
value={((override.y ?? slot.y) * 100).toFixed(1)}
onChange={(event) => onPercentChange('y')(Number(event.target.value))}
style={numberInputStyle}
<StepperInput
value={(override.y ?? slot.y) * 100}
min={0}
max={100}
step={0.5}
onChange={(val) => onPercentChange('y')(val)}
/>
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
Breite (%)
</Text>
<input
type="number"
step="0.5"
min="10"
max="100"
value={((override.w ?? slot.w) * 100).toFixed(1)}
onChange={(event) => onPercentChange('w')(Number(event.target.value))}
style={numberInputStyle}
<StepperInput
value={(override.w ?? slot.w) * 100}
min={10}
max={100}
step={0.5}
onChange={(val) => onPercentChange('w')(val)}
/>
</YStack>
</XStack>
<XStack space="$2">
<XStack space="$3">
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
Font Size (px)
</Text>
<input
type="number"
step="1"
min="8"
max="200"
value={(override.fontSize ?? slot.fontSize ?? 16).toFixed(0)}
onChange={(event) => onFontSizeChange(Number(event.target.value))}
style={numberInputStyle}
/>
<StepperInput value={override.fontSize ?? slot.fontSize ?? 16} min={8} max={200} step={1} onChange={(val) => onFontSizeChange(val)} />
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
Font Family
</Text>
<select
value={override.fontFamily ?? slot.fontFamily ?? ''}
onChange={(event) => onFontFamilyChange(event.target.value)}
style={selectStyle}
>
<option value="">{t('events.qr.defaultFont', 'Standard')}</option>
{fontOptions.map((family) => (
<option key={family} value={family}>
{family}
</option>
))}
</select>
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
{t('events.qr.fontColor', 'Schriftfarbe')}
</Text>
<XStack space="$2" alignItems="center">
<input
type="color"
value={override.color ?? slot.color ?? '#0f172a'}
onChange={(event) => onUpdateSlot(slotKey, { color: event.target.value })}
style={{ width: 48, height: 36, border: '1px solid #e5e7eb', borderRadius: 8, padding: 0, background: '#fff' }}
/>
<input
type="text"
value={override.color ?? slot.color ?? ''}
placeholder="#0f172a"
onChange={(event) => onUpdateSlot(slotKey, { color: event.target.value })}
style={numberInputStyle}
/>
</XStack>
</YStack>
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
Line Height
</Text>
<input
type="number"
step="0.05"
min="0.8"
max="3"
value={(override.lineHeight ?? slot.lineHeight ?? 1.35).toFixed(2)}
onChange={(event) => onLineHeightChange(Number(event.target.value))}
style={numberInputStyle}
/>
<StepperInput value={override.lineHeight ?? slot.lineHeight ?? 1.35} min={0.8} max={3} step={0.05} onChange={(val) => onLineHeightChange(val)} />
</YStack>
</XStack>
<XStack space="$2">
<YStack flex={1} space="$1">
<Text fontSize="$xs" color="#6b7280">
Align
@@ -1263,25 +1351,10 @@ function LayoutControls({
<option value="right">{t('common.right', 'Rechts')}</option>
</select>
</YStack>
<YStack flex={1} />
<YStack flex={1} />
<YStack flex={1} />
</XStack>
<YStack space="$1">
<Text fontSize="$xs" color="#6b7280">
Font Family
</Text>
<select
value={override.fontFamily ?? slot.fontFamily ?? ''}
onChange={(event) => onFontFamilyChange(event.target.value)}
style={selectStyle}
>
<option value="">{t('events.qr.defaultFont', 'Standard')}</option>
{fontOptions.map((family) => (
<option key={family} value={family}>
{family}
</option>
))}
</select>
</YStack>
</YStack>
);
};