using pressables to change numerical values
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user