Files
flxn-app/src/components/avatar.tsx
2025-10-11 14:47:03 -05:00

149 lines
3.4 KiB
TypeScript

import {
Avatar as MantineAvatar,
AvatarProps as MantineAvatarProps,
Paper,
Modal,
Image,
Group,
Text,
ActionIcon,
} from "@mantine/core";
import { useState } from "react";
import { XIcon } from "@phosphor-icons/react";
interface AvatarProps
extends Omit<MantineAvatarProps, "radius" | "color" | "size"> {
name?: string;
size?: number;
radius?: string | number;
withBorder?: boolean;
disableFullscreen?: boolean;
contain?: boolean;
}
const Avatar = ({
name,
size = 35,
radius = "100%",
withBorder = true,
disableFullscreen = false,
contain = false,
...props
}: AvatarProps) => {
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
const hasImage = Boolean(props.src);
const handleAvatarClick = () => {
if (hasImage && !disableFullscreen) {
setIsFullscreenOpen(true);
}
};
return (
<>
<Paper
p={size / 20}
radius={radius}
withBorder={withBorder}
style={{
cursor: hasImage && !disableFullscreen ? 'pointer' : 'default',
transition: 'transform 0.15s ease',
}}
onMouseEnter={(e) => {
if (hasImage && !disableFullscreen) {
e.currentTarget.style.transform = 'scale(1.02)';
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'scale(1)';
}}
onClick={handleAvatarClick}
>
<MantineAvatar
alt={name}
key={name}
name={name}
color="initials"
size={size}
radius={radius}
w={size}
styles={{
image: {
objectFit: contain ? 'contain' : 'cover',
},
}}
{...props}
/>
</Paper>
<Modal
opened={isFullscreenOpen}
onClose={() => setIsFullscreenOpen(false)}
size="auto"
centered
withCloseButton={false}
overlayProps={{
backgroundOpacity: 0.9,
blur: 2,
}}
styles={{
content: {
background: 'transparent',
border: 'none',
},
body: {
padding: 0,
},
}}
>
<div style={{ position: 'relative', maxWidth: '90vw', maxHeight: '90vh' }}>
<ActionIcon
variant="filled"
color="dark"
size="lg"
radius="xl"
style={{
position: 'absolute',
top: -10,
right: -10,
zIndex: 1000,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
}}
onClick={() => setIsFullscreenOpen(false)}
>
<XIcon size={18} color="white" />
</ActionIcon>
<Image
src={props.src}
alt={name}
fit="contain"
style={{
borderRadius: 8,
maxWidth: '90vw',
maxHeight: '90vh',
}}
/>
<Group
justify="center"
mt="md"
style={{
position: 'absolute',
bottom: -50,
left: '50%',
transform: 'translateX(-50%)',
}}
>
<Text c="white" size="sm" fw={500}>
{name}
</Text>
</Group>
</div>
</Modal>
</>
);
};
export default Avatar;