pranav8tripathi@gmail.com
init
3937b35
import React, { useState, useRef } from 'react';
import { Box, Fab, Slide, useMediaQuery, useTheme } from '@mui/material';
import ChatIcon from '@mui/icons-material/Chat';
import CloseIcon from '@mui/icons-material/Close';
import ChatInterface from './ChatInterface';
type FloatingChatProps = {
onClose: () => void;
};
const FloatingChat: React.FC<FloatingChatProps> = ({ onClose }) => {
const [isOpen, setIsOpen] = useState(true);
const handleClose = () => {
setIsOpen(false);
onClose();
};
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const chatRef = useRef<HTMLDivElement>(null);
const toggleChat = () => setIsOpen(prev => !prev);
const ANIM_DURATION = 300;
const FAB_SIZE = 60;
const FAB_GAP = isMobile ? 16 : 24;
return (
<Box
sx={{
position: 'fixed',
bottom: 0,
right: 0,
zIndex: 1400,
pointerEvents: 'none', // children control pointerEvents individually
}}
>
{/* Slide wrapper: NOTE -> overflow must be visible so internal header isn't clipped */}
<Slide
direction="up"
in={isOpen}
mountOnEnter
unmountOnExit
timeout={ANIM_DURATION}
>
<Box
ref={chatRef}
// sx={{
// position: 'fixed',
// right: isMobile ? FAB_GAP : FAB_GAP,
// bottom: FAB_GAP,
// width: isMobile ? '0vw' : 1410,
// height: isMobile ? '0vh' : 1410,
// maxHeight: 'calc(100vh - 48px)',
// borderRadius: 2,
// // Crucial: allow overflow visible so header (and shadows) aren't clipped during transform
// overflow: 'visible',
// display: 'flex',
// flexDirection: 'column',
// boxShadow: 12,
// backgroundColor: theme.palette.background.paper,
// zIndex: 1410,
// pointerEvents: 'auto',
// // Help browser optimize the transform during animation
// willChange: 'transform, opacity',
// transformOrigin: 'bottom right',
// }}
sx={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
borderRadius: 0, // remove rounded corners since it's full screen
overflow: 'hidden', // keeps ChatInterface properly contained
display: 'flex',
flexDirection: 'column',
boxShadow: 'none', // optional, remove shadow since it's full viewport
backgroundColor: theme.palette.background.paper,
zIndex: 1410,
pointerEvents: 'auto',
willChange: 'transform, opacity',
transformOrigin: 'bottom right',
}}
>
<ChatInterface onClose={handleClose} />
</Box>
</Slide>
{/* FAB wrapper (keeps FAB mounted during transition) */}
<Box
sx={{
position: 'fixed',
right: isMobile ? FAB_GAP : FAB_GAP,
bottom: FAB_GAP,
zIndex: 1405,
pointerEvents: 'none',
}}
>
<Fab
color="primary"
aria-label="chat"
onClick={toggleChat}
sx={{
width: FAB_SIZE,
height: FAB_SIZE,
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
'&:hover': { backgroundColor: theme.palette.primary.dark },
transition: `transform ${ANIM_DURATION}ms ease, opacity ${ANIM_DURATION}ms ease`,
pointerEvents: isOpen ? 'none' : 'auto', // make non-clickable while chat is open
opacity: isOpen ? 0 : 1,
transform: isOpen ? 'translateY(12px) scale(0.98)' : 'translateY(0) scale(1)',
boxShadow: 6,
}}
>
{isOpen ? <CloseIcon /> : <ChatIcon fontSize="large" />}
</Fab>
</Box>
</Box>
);
};
export default FloatingChat;