using pressables to change numerical values
This commit is contained in:
@@ -306,6 +306,7 @@ type SlotDefinition = {
|
|||||||
fontFamily?: string;
|
fontFamily?: string;
|
||||||
lineHeight?: number;
|
lineHeight?: number;
|
||||||
align?: 'left' | 'center' | 'right';
|
align?: 'left' | 'center' | 'right';
|
||||||
|
color?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SlotOverrides = Record<string, Partial<SlotDefinition>>;
|
type SlotOverrides = Record<string, Partial<SlotDefinition>>;
|
||||||
@@ -345,6 +346,7 @@ function normalizeSlotOverrides(raw: unknown): SlotOverrides {
|
|||||||
fontSize: toNumber(slot.fontSize),
|
fontSize: toNumber(slot.fontSize),
|
||||||
lineHeight: toNumber(slot.lineHeight),
|
lineHeight: toNumber(slot.lineHeight),
|
||||||
fontFamily: typeof slot.fontFamily === 'string' ? slot.fontFamily : undefined,
|
fontFamily: typeof slot.fontFamily === 'string' ? slot.fontFamily : undefined,
|
||||||
|
color: typeof slot.color === 'string' ? slot.color : undefined,
|
||||||
align,
|
align,
|
||||||
};
|
};
|
||||||
return acc;
|
return acc;
|
||||||
@@ -392,11 +394,11 @@ function renderEventName(name: TenantEvent['name'] | null | undefined): string |
|
|||||||
|
|
||||||
function getDefaultSlots(): Record<string, SlotDefinition> {
|
function getDefaultSlots(): Record<string, SlotDefinition> {
|
||||||
return {
|
return {
|
||||||
headline: { x: 0.08, y: 0.08, w: 0.84, fontSize: 30, fontWeight: 800, fontFamily: 'Playfair Display', 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.16, w: 0.8, fontSize: 18, fontWeight: 600, fontFamily: 'Montserrat', 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.22, w: 0.8, fontSize: 16, fontFamily: 'Lora', lineHeight: 1.4, 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 },
|
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
|
const baseSlots = isFoldable
|
||||||
? {
|
? {
|
||||||
headline: { x: 0.1, y: 0.1, w: 0.8, fontSize: 28, fontWeight: 800, fontFamily: 'Playfair Display', align: 'center' },
|
headline: { x: 0.08, y: 0.15, w: 0.84, fontSize: 50, 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' },
|
subtitle: { x: 0.1, y: 0.21, w: 0.8, fontSize: 37, 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' },
|
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 },
|
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();
|
: getDefaultSlots();
|
||||||
|
|
||||||
@@ -452,6 +454,7 @@ function applySlotOverrides(baseSlots: Record<string, SlotDefinition>, overrides
|
|||||||
fontFamily: override.fontFamily ?? base.fontFamily,
|
fontFamily: override.fontFamily ?? base.fontFamily,
|
||||||
lineHeight: override.lineHeight ?? base.lineHeight,
|
lineHeight: override.lineHeight ?? base.lineHeight,
|
||||||
align: override.align ?? base.align,
|
align: override.align ?? base.align,
|
||||||
|
color: override.color ?? base.color,
|
||||||
};
|
};
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
@@ -544,7 +547,7 @@ function buildFabricOptions({
|
|||||||
align: slot.align ?? 'center',
|
align: slot.align ?? 'center',
|
||||||
lineHeight: slot.lineHeight ?? 1.35,
|
lineHeight: slot.lineHeight ?? 1.35,
|
||||||
content,
|
content,
|
||||||
fill: textColor,
|
fill: slot.color ?? textColor,
|
||||||
fontFamily: slot.fontFamily,
|
fontFamily: slot.fontFamily,
|
||||||
rotation,
|
rotation,
|
||||||
});
|
});
|
||||||
@@ -652,7 +655,11 @@ function BackgroundStep({
|
|||||||
null;
|
null;
|
||||||
|
|
||||||
const isFoldable = (resolvedLayout?.panel_mode ?? '').toLowerCase() === 'double-mirror';
|
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 disablePresets = isFoldable;
|
||||||
const gradientPresets = [
|
const gradientPresets = [
|
||||||
{ angle: 180, stops: ['#F8FAFC', '#EEF2FF', '#F8FAFC'], label: 'Soft Lilac' },
|
{ angle: 180, stops: ['#F8FAFC', '#EEF2FF', '#F8FAFC'], label: 'Soft Lilac' },
|
||||||
@@ -1129,12 +1136,13 @@ function LayoutControls({
|
|||||||
}, [tenantFonts]);
|
}, [tenantFonts]);
|
||||||
|
|
||||||
const numberInputStyle: React.CSSProperties = {
|
const numberInputStyle: React.CSSProperties = {
|
||||||
width: '100%',
|
width: 90,
|
||||||
padding: '10px 12px',
|
padding: '10px 12px',
|
||||||
border: '1px solid #e5e7eb',
|
border: '1px solid #e5e7eb',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
|
appearance: 'textfield',
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectStyle: React.CSSProperties = {
|
const selectStyle: React.CSSProperties = {
|
||||||
@@ -1146,6 +1154,50 @@ function LayoutControls({
|
|||||||
background: '#fff',
|
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 renderTextSlot = (slotKey: string, label: string) => {
|
||||||
const slot = slots[slotKey];
|
const slot = slots[slotKey];
|
||||||
if (!slot) return null;
|
if (!slot) return null;
|
||||||
@@ -1175,80 +1227,116 @@ function LayoutControls({
|
|||||||
<Text fontSize="$sm" fontWeight="700" color="#111827">
|
<Text fontSize="$sm" fontWeight="700" color="#111827">
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<XStack space="$2">
|
<XStack space="$3">
|
||||||
<YStack flex={1} space="$1">
|
<YStack flex={1} space="$1">
|
||||||
<Text fontSize="$xs" color="#6b7280">
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
X (%)
|
X (%)
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<XStack space="$1" alignItems="center">
|
||||||
type="number"
|
<Pressable onPress={() => onPercentChange('x')(((override.x ?? slot.x) * 100) - 0.5)}>
|
||||||
step="0.5"
|
<XStack width={36} height={36} borderRadius={10} alignItems="center" justifyContent="center" borderWidth={1} borderColor="#e5e7eb" backgroundColor="#fff">
|
||||||
min="0"
|
<Text fontSize="$md" fontWeight="800" color="#111827">
|
||||||
max="100"
|
–
|
||||||
value={((override.x ?? slot.x) * 100).toFixed(1)}
|
</Text>
|
||||||
onChange={(event) => onPercentChange('x')(Number(event.target.value))}
|
</XStack>
|
||||||
style={numberInputStyle}
|
</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>
|
||||||
<YStack flex={1} space="$1">
|
<YStack flex={1} space="$1">
|
||||||
<Text fontSize="$xs" color="#6b7280">
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
Y (%)
|
Y (%)
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<StepperInput
|
||||||
type="number"
|
value={(override.y ?? slot.y) * 100}
|
||||||
step="0.5"
|
min={0}
|
||||||
min="0"
|
max={100}
|
||||||
max="100"
|
step={0.5}
|
||||||
value={((override.y ?? slot.y) * 100).toFixed(1)}
|
onChange={(val) => onPercentChange('y')(val)}
|
||||||
onChange={(event) => onPercentChange('y')(Number(event.target.value))}
|
|
||||||
style={numberInputStyle}
|
|
||||||
/>
|
/>
|
||||||
</YStack>
|
</YStack>
|
||||||
<YStack flex={1} space="$1">
|
<YStack flex={1} space="$1">
|
||||||
<Text fontSize="$xs" color="#6b7280">
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
Breite (%)
|
Breite (%)
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<StepperInput
|
||||||
type="number"
|
value={(override.w ?? slot.w) * 100}
|
||||||
step="0.5"
|
min={10}
|
||||||
min="10"
|
max={100}
|
||||||
max="100"
|
step={0.5}
|
||||||
value={((override.w ?? slot.w) * 100).toFixed(1)}
|
onChange={(val) => onPercentChange('w')(val)}
|
||||||
onChange={(event) => onPercentChange('w')(Number(event.target.value))}
|
|
||||||
style={numberInputStyle}
|
|
||||||
/>
|
/>
|
||||||
</YStack>
|
</YStack>
|
||||||
</XStack>
|
</XStack>
|
||||||
|
|
||||||
<XStack space="$2">
|
<XStack space="$3">
|
||||||
<YStack flex={1} space="$1">
|
<YStack flex={1} space="$1">
|
||||||
<Text fontSize="$xs" color="#6b7280">
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
Font Size (px)
|
Font Size (px)
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<StepperInput value={override.fontSize ?? slot.fontSize ?? 16} min={8} max={200} step={1} onChange={(val) => onFontSizeChange(val)} />
|
||||||
type="number"
|
</YStack>
|
||||||
step="1"
|
<YStack flex={1} space="$1">
|
||||||
min="8"
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
max="200"
|
Font Family
|
||||||
value={(override.fontSize ?? slot.fontSize ?? 16).toFixed(0)}
|
</Text>
|
||||||
onChange={(event) => onFontSizeChange(Number(event.target.value))}
|
<select
|
||||||
style={numberInputStyle}
|
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>
|
||||||
<YStack flex={1} space="$1">
|
<YStack flex={1} space="$1">
|
||||||
<Text fontSize="$xs" color="#6b7280">
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
Line Height
|
Line Height
|
||||||
</Text>
|
</Text>
|
||||||
<input
|
<StepperInput value={override.lineHeight ?? slot.lineHeight ?? 1.35} min={0.8} max={3} step={0.05} onChange={(val) => onLineHeightChange(val)} />
|
||||||
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}
|
|
||||||
/>
|
|
||||||
</YStack>
|
</YStack>
|
||||||
|
</XStack>
|
||||||
|
<XStack space="$2">
|
||||||
<YStack flex={1} space="$1">
|
<YStack flex={1} space="$1">
|
||||||
<Text fontSize="$xs" color="#6b7280">
|
<Text fontSize="$xs" color="#6b7280">
|
||||||
Align
|
Align
|
||||||
@@ -1263,25 +1351,10 @@ function LayoutControls({
|
|||||||
<option value="right">{t('common.right', 'Rechts')}</option>
|
<option value="right">{t('common.right', 'Rechts')}</option>
|
||||||
</select>
|
</select>
|
||||||
</YStack>
|
</YStack>
|
||||||
|
<YStack flex={1} />
|
||||||
|
<YStack flex={1} />
|
||||||
|
<YStack flex={1} />
|
||||||
</XStack>
|
</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>
|
</YStack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user