File size: 4,058 Bytes
0e6ed99
 
 
 
 
4f5bfc3
3937b35
 
 
 
 
f3a1979
3937b35
 
 
 
 
0e6ed99
 
 
 
4f5bfc3
 
 
 
 
0e6ed99
 
 
 
09ffe59
4f5bfc3
 
 
 
0e6ed99
 
4f5bfc3
09ffe59
 
 
 
 
4f5bfc3
09ffe59
 
 
4f5bfc3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09ffe59
4f5bfc3
 
 
 
 
 
 
09ffe59
 
4f5bfc3
 
 
 
 
 
09ffe59
4f5bfc3
09ffe59
3937b35
09ffe59
 
0e6ed99
4f5bfc3
 
09ffe59
4f5bfc3
 
 
 
 
09ffe59
 
4f5bfc3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09ffe59
0e6ed99
 
 
4f5bfc3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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;