Files
fotospiel-app/resources/js/guest-v2/components/CompassHub.tsx
Codex Agent fa630e335d
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
Update guest PWA v2 UI and likes
2026-02-05 15:09:19 +01:00

190 lines
5.4 KiB
TypeScript

import React from 'react';
import { XStack, YStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { X } from 'lucide-react';
import { useGuestThemeVariant } from '../lib/guestTheme';
import { getBentoSurfaceTokens } from '../lib/bento';
export type CompassAction = {
key: string;
label: string;
icon?: React.ReactNode;
onPress?: () => void;
};
type CompassHubProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
quadrants: [CompassAction, CompassAction, CompassAction, CompassAction];
centerAction: CompassAction;
title?: string;
};
const quadrantPositions: Array<{
top?: number;
right?: number;
bottom?: number;
left?: number;
}> = [
{ top: 0, left: 0 },
{ top: 0, right: 0 },
{ bottom: 0, left: 0 },
{ bottom: 0, right: 0 },
];
export default function CompassHub({
open,
onOpenChange,
quadrants,
centerAction,
title = 'Quick jump',
}: CompassHubProps) {
const close = () => onOpenChange(false);
const { isDark } = useGuestThemeVariant();
const bentoSurface = getBentoSurfaceTokens(isDark);
const tileShadow = isDark
? '0 10px 0 rgba(2, 6, 23, 0.55), 0 20px 24px rgba(2, 6, 23, 0.45)'
: '0 10px 0 rgba(15, 23, 42, 0.18), 0 18px 22px rgba(15, 23, 42, 0.16)';
const [visible, setVisible] = React.useState(open);
const [closing, setClosing] = React.useState(false);
React.useEffect(() => {
if (open) {
setVisible(true);
setClosing(false);
return;
}
if (!visible) return;
setClosing(true);
const timeout = window.setTimeout(() => {
setVisible(false);
setClosing(false);
}, 520);
return () => {
window.clearTimeout(timeout);
};
}, [open, visible]);
if (!visible) {
return null;
}
return (
<YStack position="fixed" inset={0} zIndex={100000} pointerEvents="box-none">
<YStack
position="absolute"
inset={0}
backgroundColor={isDark ? 'rgba(15, 23, 42, 0.55)' : 'rgba(15, 23, 42, 0.22)'}
pointerEvents="auto"
onPress={close}
onClick={close}
onMouseDown={close}
onTouchStart={close}
/>
<YStack
position="absolute"
inset={0}
padding={24}
alignItems="center"
justifyContent="center"
pointerEvents="box-none"
>
<YStack
alignItems="center"
justifyContent="center"
pointerEvents="auto"
>
<YStack
key={closing ? 'compass-out' : 'compass-in'}
width={280}
height={280}
position="relative"
className={closing ? 'guest-compass-flyout' : 'guest-compass-flyin'}
>
<Button
size="$3"
circular
backgroundColor={bentoSurface.backgroundColor}
borderWidth={1}
borderBottomWidth={3}
borderColor={bentoSurface.borderColor}
borderBottomColor={bentoSurface.borderBottomColor}
onPress={close}
aria-label="Close compass"
style={{
position: 'absolute',
right: -18,
top: -18,
boxShadow: tileShadow,
transform: 'rotate(-6deg)',
zIndex: 2,
}}
>
<X size={16} color={isDark ? '#F8FAFF' : '#0F172A'} />
</Button>
{quadrants.map((action, index) => (
<Button
key={action.key}
onPress={() => {
action.onPress?.();
close();
}}
width={120}
height={120}
borderRadius={24}
backgroundColor={bentoSurface.backgroundColor}
borderWidth={1}
borderBottomWidth={3}
borderColor={bentoSurface.borderColor}
borderBottomColor={bentoSurface.borderBottomColor}
position="absolute"
{...quadrantPositions[index]}
style={{ boxShadow: tileShadow }}
>
<YStack alignItems="center" gap="$2">
{action.icon}
<Text fontSize="$3" fontWeight="$7">
{action.label}
</Text>
</YStack>
</Button>
))}
<Button
onPress={() => {
centerAction.onPress?.();
close();
}}
width={90}
height={90}
borderRadius={45}
backgroundColor="$primary"
borderWidth={1}
borderBottomWidth={3}
borderColor={bentoSurface.borderColor}
borderBottomColor={bentoSurface.borderBottomColor}
position="absolute"
top="50%"
left="50%"
style={{
transform: 'translate(-45px, -45px)',
boxShadow: tileShadow,
}}
>
<YStack alignItems="center" gap="$1">
{centerAction.icon}
<Text fontSize="$2" fontWeight="$7" color="white">
{centerAction.label}
</Text>
</YStack>
</Button>
</YStack>
</YStack>
</YStack>
</YStack>
);
}