mirror of https://gitee.com/answerdev/answer.git
fix: mobile navigation adaptations
This commit is contained in:
parent
1ef3b2bc9f
commit
84c753da78
|
@ -0,0 +1,69 @@
|
|||
import { FC, memo } from 'react';
|
||||
import { Nav, Dropdown } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
|
||||
import { Avatar, Icon } from '@answer/components';
|
||||
|
||||
interface Props {
|
||||
redDot;
|
||||
userInfo;
|
||||
logOut: () => void;
|
||||
}
|
||||
|
||||
const Index: FC<Props> = ({ redDot, userInfo, logOut }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Nav.Link
|
||||
as={NavLink}
|
||||
to="/users/notifications/inbox"
|
||||
className="icon-link d-flex align-items-center justify-content-center p-0 me-3 position-relative">
|
||||
<div className="text-white text-opacity-75">
|
||||
<Icon name="bell-fill" className="fs-4" />
|
||||
</div>
|
||||
{(redDot?.inbox || 0) > 0 && <div className="unread-dot bg-danger" />}
|
||||
</Nav.Link>
|
||||
|
||||
<Nav.Link
|
||||
as={Link}
|
||||
to="/users/notifications/achievement"
|
||||
className="icon-link d-flex align-items-center justify-content-center p-0 me-3 position-relative">
|
||||
<div className="text-white text-opacity-75">
|
||||
<Icon name="trophy-fill" className="fs-4" />
|
||||
</div>
|
||||
{(redDot?.achievement || 0) > 0 && (
|
||||
<div className="unread-dot bg-danger" />
|
||||
)}
|
||||
</Nav.Link>
|
||||
|
||||
<Dropdown align="end">
|
||||
<Dropdown.Toggle
|
||||
variant="success"
|
||||
id="dropdown-basic"
|
||||
as="a"
|
||||
className="no-toggle pointer">
|
||||
<Avatar size="36px" avatar={userInfo?.avatar} />
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item href={`/users/${userInfo.username}`}>
|
||||
{t('header.nav.profile')}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href="/users/settings/profile">
|
||||
{t('header.nav.setting')}
|
||||
</Dropdown.Item>
|
||||
{userInfo?.is_admin ? (
|
||||
<Dropdown.Item href="/admin">{t('header.nav.admin')}</Dropdown.Item>
|
||||
) : null}
|
||||
<Dropdown.Divider />
|
||||
<Dropdown.Item onClick={logOut}>
|
||||
{t('header.nav.logout')}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Index);
|
|
@ -14,8 +14,8 @@
|
|||
color: #fff;
|
||||
}
|
||||
&.icon-link {
|
||||
width: 46px;
|
||||
height: 38px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
.placeholder-search {
|
||||
|
@ -28,4 +28,40 @@
|
|||
color: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
}
|
||||
|
||||
.answer-navBar {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border: none;
|
||||
}
|
||||
.answer-navBar:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.lg-none {
|
||||
display: none!important;
|
||||
}
|
||||
|
||||
.hr {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 992.9px) {
|
||||
#header {
|
||||
.nav-grow {
|
||||
flex-grow: 1!important;
|
||||
}
|
||||
|
||||
.lg-none {
|
||||
display: flex!important;
|
||||
}
|
||||
|
||||
.w-75 {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,22 @@ import {
|
|||
FormControl,
|
||||
Button,
|
||||
Col,
|
||||
Dropdown,
|
||||
} from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSearchParams, NavLink, Link, useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
useSearchParams,
|
||||
NavLink,
|
||||
Link,
|
||||
useNavigate,
|
||||
useLocation,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { Avatar, Icon } from '@answer/components';
|
||||
import { userInfoStore, siteInfoStore, interfaceStore } from '@answer/stores';
|
||||
import { logout, useQueryNotificationStatus } from '@answer/api';
|
||||
import Storage from '@answer/utils/storage';
|
||||
|
||||
import NavItems from './components/NavItems';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const Header: FC = () => {
|
||||
|
@ -29,6 +35,7 @@ const Header: FC = () => {
|
|||
const siteInfo = siteInfoStore((state) => state.siteInfo);
|
||||
const { interface: interfaceInfo } = interfaceStore();
|
||||
const { data: redDot } = useQueryNotificationStatus();
|
||||
const location = useLocation();
|
||||
const handleInput = (val) => {
|
||||
setSearch(val);
|
||||
};
|
||||
|
@ -45,22 +52,61 @@ const Header: FC = () => {
|
|||
handleInput(q);
|
||||
}
|
||||
}, [q]);
|
||||
|
||||
useEffect(() => {
|
||||
const collapse = document.querySelector('#navBarContent');
|
||||
if (collapse && collapse.classList.contains('show')) {
|
||||
const toogle = document.querySelector('#navBarToggle') as HTMLElement;
|
||||
if (toogle) {
|
||||
toogle?.click();
|
||||
}
|
||||
}
|
||||
}, [location.pathname]);
|
||||
|
||||
return (
|
||||
<Navbar variant="dark" expand="lg" className="sticky-top" id="header">
|
||||
<Container className="d-flex align-items-center">
|
||||
<Navbar.Brand href="/">
|
||||
{interfaceInfo.logo ? (
|
||||
<img
|
||||
className="logo rounded-1 me-0"
|
||||
src={interfaceInfo.logo}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<span>{siteInfo.name || 'Answer'}</span>
|
||||
)}
|
||||
</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="navBarContent" />
|
||||
<Navbar.Toggle
|
||||
aria-controls="navBarContent"
|
||||
className="answer-navBar me-2"
|
||||
id="navBarToggle"
|
||||
/>
|
||||
|
||||
<div className="left-wrap d-flex justify-content-between align-items-center nav-grow">
|
||||
<Navbar.Brand to="/" as={Link}>
|
||||
{interfaceInfo.logo ? (
|
||||
<img
|
||||
className="logo rounded-1 me-0"
|
||||
src={interfaceInfo.logo}
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<span>{siteInfo.name || 'Answer'}</span>
|
||||
)}
|
||||
</Navbar.Brand>
|
||||
|
||||
{/* mobile nav */}
|
||||
<div className="d-flex lg-none align-items-center flex-lg-nowrap">
|
||||
{user?.username ? (
|
||||
<NavItems redDot={redDot} userInfo={user} logOut={handleLogout} />
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="link"
|
||||
className="me-2 text-white"
|
||||
href="/users/login">
|
||||
{t('btns.login')}
|
||||
</Button>
|
||||
<Button variant="light" href="/users/register">
|
||||
{t('btns.signup')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Navbar.Collapse id="navBarContent" className="me-auto">
|
||||
<hr className="hr lg-none mb-2" style={{ marginTop: '12px' }} />
|
||||
<Col md={4}>
|
||||
<Nav>
|
||||
<NavLink className="nav-link" to="/questions">
|
||||
|
@ -74,9 +120,10 @@ const Header: FC = () => {
|
|||
</NavLink>
|
||||
</Nav>
|
||||
</Col>
|
||||
<hr className="hr lg-none mt-2" />
|
||||
|
||||
<Col md={4} className="d-none d-sm-flex justify-content-center">
|
||||
<Form action="/search" className="w-75 px-2">
|
||||
<Col lg={4} className="d-flex justify-content-center">
|
||||
<Form action="/search" className="w-75 px-0 px-lg-2">
|
||||
<FormControl
|
||||
placeholder={t('header.search.placeholder')}
|
||||
className="text-white placeholder-search"
|
||||
|
@ -87,69 +134,32 @@ const Header: FC = () => {
|
|||
</Form>
|
||||
</Col>
|
||||
|
||||
<Nav.Item className="lg-none mt-3 pb-1">
|
||||
<Link
|
||||
to="/questions/ask"
|
||||
className="text-capitalize text-nowrap btn btn-light">
|
||||
{t('btns.add_question')}
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
{/* pc nav */}
|
||||
<Col
|
||||
md={4}
|
||||
className="d-flex justify-content-start justify-content-sm-end">
|
||||
lg={4}
|
||||
className="d-none d-lg-flex justify-content-start justify-content-sm-end">
|
||||
{user?.username ? (
|
||||
<Nav className="d-flex align-items-center flex-lg-nowrap">
|
||||
<Nav.Item className="me-2">
|
||||
<Nav.Item className="me-3">
|
||||
<Link
|
||||
to="/questions/ask"
|
||||
className="text-capitalize text-nowrap btn btn-light">
|
||||
{t('btns.add_question')}
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<Nav.Link
|
||||
as={NavLink}
|
||||
to="/users/notifications/inbox"
|
||||
className="icon-link d-flex align-items-center justify-content-center p-0 me-2 position-relative">
|
||||
<div className="text-white text-opacity-75">
|
||||
<Icon name="bell-fill" className="fs-5" />
|
||||
</div>
|
||||
{(redDot?.inbox || 0) > 0 && (
|
||||
<div className="unread-dot bg-danger" />
|
||||
)}
|
||||
</Nav.Link>
|
||||
|
||||
<Nav.Link
|
||||
as={Link}
|
||||
to="/users/notifications/achievement"
|
||||
className="icon-link d-flex align-items-center justify-content-center p-0 me-2 position-relative">
|
||||
<div className="text-white text-opacity-75">
|
||||
<Icon name="trophy-fill" className="fs-5" />
|
||||
</div>
|
||||
{(redDot?.achievement || 0) > 0 && (
|
||||
<div className="unread-dot bg-danger" />
|
||||
)}
|
||||
</Nav.Link>
|
||||
|
||||
<Dropdown align="end">
|
||||
<Dropdown.Toggle
|
||||
variant="success"
|
||||
id="dropdown-basic"
|
||||
as="a"
|
||||
className="no-toggle pointer">
|
||||
<Avatar size="36px" avatar={user?.avatar} />
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item href={`/users/${user.username}`}>
|
||||
{t('header.nav.profile')}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href="/users/settings/profile">
|
||||
{t('header.nav.setting')}
|
||||
</Dropdown.Item>
|
||||
{user?.is_admin ? (
|
||||
<Dropdown.Item href="/admin">
|
||||
{t('header.nav.admin')}
|
||||
</Dropdown.Item>
|
||||
) : null}
|
||||
<Dropdown.Divider />
|
||||
<Dropdown.Item onClick={handleLogout}>
|
||||
{t('header.nav.logout')}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
<NavItems
|
||||
redDot={redDot}
|
||||
userInfo={user}
|
||||
logOut={handleLogout}
|
||||
/>
|
||||
</Nav>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
@ -49,8 +49,8 @@ a {
|
|||
height: 18px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 22px;
|
||||
top: 3px;
|
||||
left: 20px;
|
||||
top: 0px;
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue