94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import React from 'react';
|
|
import { YStack, XStack } from '@tamagui/stacks';
|
|
import { Button } from '@tamagui/button';
|
|
import { SizableText as Text } from '@tamagui/text';
|
|
import { useGuestThemeVariant } from '../lib/guestTheme';
|
|
|
|
type FabAction = {
|
|
key: string;
|
|
label: string;
|
|
icon: React.ReactNode;
|
|
onPress?: () => void;
|
|
};
|
|
|
|
type FabActionRingProps = {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
actions: FabAction[];
|
|
};
|
|
|
|
const positions = [
|
|
{ x: 0, y: -120 },
|
|
{ x: -70, y: -100 },
|
|
{ x: -120, y: -24 },
|
|
{ x: -92, y: 52 },
|
|
];
|
|
|
|
export default function FabActionRing({ open, onOpenChange, actions }: FabActionRingProps) {
|
|
const { isDark } = useGuestThemeVariant();
|
|
const borderColor = isDark ? 'rgba(255,255,255,0.18)' : 'rgba(15, 23, 42, 0.12)';
|
|
const surfaceColor = isDark ? 'rgba(12, 16, 32, 0.92)' : 'rgba(255, 255, 255, 0.95)';
|
|
const textColor = isDark ? '#F8FAFF' : '#0F172A';
|
|
const shadow = isDark ? '0 16px 28px rgba(2, 6, 23, 0.55)' : '0 14px 24px rgba(15, 23, 42, 0.18)';
|
|
const ringActions = actions.slice(0, positions.length);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<YStack position="fixed" inset={0} zIndex={1090} pointerEvents="box-none">
|
|
<YStack
|
|
position="absolute"
|
|
inset={0}
|
|
backgroundColor={isDark ? 'rgba(2,6,23,0.5)' : 'rgba(15,23,42,0.2)'}
|
|
onPress={() => onOpenChange(false)}
|
|
/>
|
|
<YStack position="absolute" bottom={24} right={20} pointerEvents="box-none">
|
|
{ringActions.map((action, index) => {
|
|
const offset = positions[index];
|
|
return (
|
|
<XStack
|
|
key={action.key}
|
|
alignItems="center"
|
|
gap="$2"
|
|
position="absolute"
|
|
style={{
|
|
transform: `translate(${offset.x}px, ${offset.y}px)`,
|
|
opacity: open ? 1 : 0,
|
|
transition: 'transform 260ms ease, opacity 200ms ease',
|
|
}}
|
|
>
|
|
<Button
|
|
size="$3"
|
|
circular
|
|
backgroundColor={surfaceColor}
|
|
borderWidth={1}
|
|
borderColor={borderColor}
|
|
onPress={() => {
|
|
onOpenChange(false);
|
|
action.onPress?.();
|
|
}}
|
|
style={{ boxShadow: shadow }}
|
|
>
|
|
{action.icon}
|
|
</Button>
|
|
<XStack
|
|
paddingHorizontal="$3"
|
|
paddingVertical="$2"
|
|
borderRadius="$pill"
|
|
backgroundColor={surfaceColor}
|
|
borderWidth={1}
|
|
borderColor={borderColor}
|
|
style={{ boxShadow: shadow }}
|
|
>
|
|
<Text fontSize="$2" fontWeight="$6" color={textColor}>
|
|
{action.label}
|
|
</Text>
|
|
</XStack>
|
|
</XStack>
|
|
);
|
|
})}
|
|
</YStack>
|
|
</YStack>
|
|
);
|
|
}
|