Improve mobile conversation drawer layering and empty state (#59)
Unit Tests / test (pull_request) Successful in 13s
Unit Tests / test (pull_request) Successful in 13s
Make the dashboard sidebar a clearer fixed sheet on small screens with an explicit close control and empty-list guidance so it is less likely to look like a blank page.
This commit is contained in:
@@ -2,7 +2,7 @@ import React, { useContext, useEffect, useRef, useState } from "react";
|
|||||||
import styled, { ThemeContext } from "styled-components";
|
import styled, { ThemeContext } from "styled-components";
|
||||||
import { Formik, Form, Field } from "formik";
|
import { Formik, Form, Field } from "formik";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { AttachFile, Delete, Send, Menu } from "@mui/icons-material"; // Keeping icons for now, can replace later if needed
|
import { AttachFile, Delete, Send, Menu, Close } from "@mui/icons-material"; // Keeping icons for now, can replace later if needed
|
||||||
import { Tooltip } from "@mui/material";
|
import { Tooltip } from "@mui/material";
|
||||||
import Markdown from "markdown-to-jsx";
|
import Markdown from "markdown-to-jsx";
|
||||||
|
|
||||||
@@ -44,10 +44,15 @@ const Sidebar = styled.div<{ $isOpen: boolean }>`
|
|||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: min(85vw, 320px);
|
||||||
|
height: 100vh;
|
||||||
transform: ${({ $isOpen }) => $isOpen ? 'translateX(0)' : 'translateX(-100%)'};
|
transform: ${({ $isOpen }) => $isOpen ? 'translateX(0)' : 'translateX(-100%)'};
|
||||||
background: ${({ theme }) => theme.darkMode ? 'rgba(0, 0, 0, 0.95)' : 'rgba(255, 255, 255, 0.95)'};
|
background: ${({ theme }) => theme.darkMode ? 'rgba(0, 0, 0, 0.95)' : 'rgba(255, 255, 255, 0.95)'};
|
||||||
box-shadow: ${({ $isOpen }) => $isOpen ? '0 0 20px rgba(0,0,0,0.5)' : 'none'};
|
box-shadow: ${({ $isOpen }) => $isOpen ? '0 0 20px rgba(0,0,0,0.5)' : 'none'};
|
||||||
|
z-index: 120;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -88,7 +93,7 @@ const Overlay = styled.div<{ $isOpen: boolean }>`
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
z-index: 15;
|
z-index: 110;
|
||||||
opacity: ${({ $isOpen }) => $isOpen ? 1 : 0};
|
opacity: ${({ $isOpen }) => $isOpen ? 1 : 0};
|
||||||
pointer-events: ${({ $isOpen }) => $isOpen ? 'auto' : 'none'};
|
pointer-events: ${({ $isOpen }) => $isOpen ? 'auto' : 'none'};
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
@@ -98,6 +103,36 @@ const Overlay = styled.div<{ $isOpen: boolean }>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const MobileSidebarHeader = styled.div`
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: ${({ theme }) => theme.colors.text};
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MobileSidebarCloseButton = styled.button`
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: ${({ theme }) => theme.colors.text};
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 0.3rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const MainContent = styled.div`
|
const MainContent = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -384,6 +419,12 @@ const AsyncDashboardInner = (): JSX.Element => {
|
|||||||
</MobileSidebarToggle>
|
</MobileSidebarToggle>
|
||||||
|
|
||||||
<Sidebar $isOpen={isSidebarOpen}>
|
<Sidebar $isOpen={isSidebarOpen}>
|
||||||
|
<MobileSidebarHeader>
|
||||||
|
<span>Conversations</span>
|
||||||
|
<MobileSidebarCloseButton onClick={() => setIsSidebarOpen(false)} aria-label="Close conversations drawer">
|
||||||
|
<Close fontSize="small" />
|
||||||
|
</MobileSidebarCloseButton>
|
||||||
|
</MobileSidebarHeader>
|
||||||
<NewChatButton onClick={() => {
|
<NewChatButton onClick={() => {
|
||||||
setSelectedConversation(undefined);
|
setSelectedConversation(undefined);
|
||||||
setIsSidebarOpen(false);
|
setIsSidebarOpen(false);
|
||||||
@@ -391,7 +432,17 @@ const AsyncDashboardInner = (): JSX.Element => {
|
|||||||
+ New Chat
|
+ New Chat
|
||||||
</NewChatButton>
|
</NewChatButton>
|
||||||
<div style={{ overflowY: 'auto', flex: 1 }}>
|
<div style={{ overflowY: 'auto', flex: 1 }}>
|
||||||
{conversations.map((convo) => (
|
{conversations.length === 0 ? (
|
||||||
|
<div style={{
|
||||||
|
color: theme?.darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)',
|
||||||
|
fontSize: '0.9rem',
|
||||||
|
lineHeight: 1.5,
|
||||||
|
padding: '0.25rem 0.5rem'
|
||||||
|
}}>
|
||||||
|
No conversations yet. Start with <strong>+ New Chat</strong>.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
conversations.map((convo) => (
|
||||||
<ConversationItem
|
<ConversationItem
|
||||||
key={convo.id}
|
key={convo.id}
|
||||||
$active={convo.id === selectedConversation}
|
$active={convo.id === selectedConversation}
|
||||||
@@ -414,7 +465,8 @@ const AsyncDashboardInner = (): JSX.Element => {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</ConversationItem>
|
</ConversationItem>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user