mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
chore: update header menu style in mobile view
This commit is contained in:
@@ -1,12 +1,10 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { NavLink, useLocation } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useLayoutStore } from "@/store/module";
|
|
||||||
import { useInboxStore } from "@/store/v1";
|
import { useInboxStore } from "@/store/v1";
|
||||||
import { Inbox_Status } from "@/types/proto/api/v2/inbox_service";
|
import { Inbox_Status } from "@/types/proto/api/v2/inbox_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import { resolution } from "@/utils/layout";
|
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import UserBanner from "./UserBanner";
|
import UserBanner from "./UserBanner";
|
||||||
|
|
||||||
@@ -19,11 +17,8 @@ interface NavLinkItem {
|
|||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const location = useLocation();
|
|
||||||
const layoutStore = useLayoutStore();
|
|
||||||
const user = useCurrentUser();
|
const user = useCurrentUser();
|
||||||
const inboxStore = useInboxStore();
|
const inboxStore = useInboxStore();
|
||||||
const showHeader = layoutStore.state.showHeader;
|
|
||||||
const hasUnreadInbox = inboxStore.inboxes.some((inbox) => inbox.status === Inbox_Status.UNREAD);
|
const hasUnreadInbox = inboxStore.inboxes.some((inbox) => inbox.status === Inbox_Status.UNREAD);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -42,22 +37,6 @@ const Header = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleWindowResize = () => {
|
|
||||||
if (window.innerWidth < resolution.sm) {
|
|
||||||
layoutStore.setHeaderStatus(false);
|
|
||||||
} else {
|
|
||||||
layoutStore.setHeaderStatus(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
handleWindowResize();
|
|
||||||
window.addEventListener("resize", handleWindowResize);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("resize", handleWindowResize);
|
|
||||||
};
|
|
||||||
}, [location]);
|
|
||||||
|
|
||||||
const homeNavLink: NavLinkItem = {
|
const homeNavLink: NavLinkItem = {
|
||||||
id: "header-home",
|
id: "header-home",
|
||||||
path: "/",
|
path: "/",
|
||||||
@@ -119,44 +98,28 @@ const Header = () => {
|
|||||||
: [exploreNavLink, signInNavLink];
|
: [exploreNavLink, signInNavLink];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<header className={`w-full h-full overflow-auto flex flex-col justify-start items-start py-4 z-30`}>
|
||||||
className={`fixed sm:sticky top-0 left-0 w-full sm:w-56 h-screen shrink-0 pointer-events-none sm:pointer-events-auto z-10 ${
|
<UserBanner />
|
||||||
showHeader && "pointer-events-auto"
|
<div className="w-full px-2 py-2 flex flex-col justify-start items-start shrink-0 space-y-2">
|
||||||
}`}
|
{navLinks.map((navLink) => (
|
||||||
>
|
<NavLink
|
||||||
<div
|
key={navLink.id}
|
||||||
className={`fixed top-0 left-0 w-full h-full max-h-screen opacity-0 pointer-events-none transition-opacity duration-300 sm:!hidden ${
|
to={navLink.path}
|
||||||
showHeader && "opacity-60 pointer-events-auto"
|
id={navLink.id}
|
||||||
}`}
|
className={({ isActive }) =>
|
||||||
onClick={() => layoutStore.setHeaderStatus(false)}
|
classNames(
|
||||||
></div>
|
"px-4 pr-5 py-2 rounded-2xl border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||||
<header
|
isActive ? "bg-white drop-shadow-sm dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||||
className={`relative w-56 sm:w-full h-full max-h-screen border-r sm:border-none dark:border-r-zinc-700 overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 z-30 bg-zinc-100 dark:bg-zinc-800 sm:bg-transparent sm:shadow-none transition-all duration-300 -translate-x-full sm:translate-x-0 ${
|
)
|
||||||
showHeader && "translate-x-0 shadow-2xl"
|
}
|
||||||
}`}
|
>
|
||||||
>
|
<>
|
||||||
<UserBanner />
|
{navLink.icon} {navLink.title}
|
||||||
<div className="w-full px-2 py-2 flex flex-col justify-start items-start shrink-0 space-y-2">
|
</>
|
||||||
{navLinks.map((navLink) => (
|
</NavLink>
|
||||||
<NavLink
|
))}
|
||||||
key={navLink.id}
|
</div>
|
||||||
to={navLink.path}
|
</header>
|
||||||
id={navLink.id}
|
|
||||||
className={({ isActive }) =>
|
|
||||||
classNames(
|
|
||||||
"px-4 pr-5 py-2 rounded-2xl border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
|
||||||
isActive ? "bg-white drop-shadow-sm dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
{navLink.icon} {navLink.title}
|
|
||||||
</>
|
|
||||||
</NavLink>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
37
web/src/components/HeaderDrawer.tsx
Normal file
37
web/src/components/HeaderDrawer.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Drawer, IconButton } from "@mui/joy";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import Header from "./Header";
|
||||||
|
import Icon from "./Icon";
|
||||||
|
|
||||||
|
const HeaderDrawer = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setOpen(false);
|
||||||
|
}, [location.pathname]);
|
||||||
|
|
||||||
|
const toggleDrawer = (inOpen: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
|
||||||
|
if (event.type === "keydown" && ((event as React.KeyboardEvent).key === "Tab" || (event as React.KeyboardEvent).key === "Shift")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpen(inOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<IconButton onClick={toggleDrawer(true)}>
|
||||||
|
<Icon.Menu className="w-5 h-auto dark:text-gray-200" />
|
||||||
|
</IconButton>
|
||||||
|
<Drawer anchor="left" open={open} onClose={toggleDrawer(false)}>
|
||||||
|
<div className="w-full px-4">
|
||||||
|
<Header />
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeaderDrawer;
|
@@ -1,5 +1,6 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useLayoutStore } from "@/store/module";
|
import { useLayoutStore } from "@/store/module";
|
||||||
|
import HeaderDrawer from "./HeaderDrawer";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -14,12 +15,13 @@ const MobileHeader = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="sticky top-0 pt-4 sm:pt-1 pb-1 mb-1 backdrop-blur bg-zinc-100 dark:bg-zinc-800 bg-opacity-70 flex @lg:hidden flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-2">
|
<div className="sticky top-0 pt-4 sm:pt-1 pb-1 mb-1 backdrop-blur bg-zinc-100 dark:bg-zinc-800 bg-opacity-70 flex @lg:hidden flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-2">
|
||||||
<div className="flex flex-row justify-start items-center mr-2 shrink-0 overflow-hidden">
|
<div className="flex flex-row justify-start items-center mr-2 shrink-0 overflow-hidden">
|
||||||
<div
|
{/* <div
|
||||||
className="flex sm:hidden flex-row justify-center items-center w-6 h-6 mr-1 shrink-0 bg-transparent"
|
className="flex sm:hidden flex-row justify-center items-center w-6 h-6 mr-1 shrink-0 bg-transparent"
|
||||||
onClick={() => layoutStore.setHeaderStatus(true)}
|
onClick={() => layoutStore.setHeaderStatus(true)}
|
||||||
>
|
>
|
||||||
<Icon.Menu className="w-5 h-auto dark:text-gray-200" />
|
<Icon.Menu className="w-5 h-auto dark:text-gray-200" />
|
||||||
</div>
|
</div> */}
|
||||||
|
<HeaderDrawer />
|
||||||
<span
|
<span
|
||||||
className="font-bold text-lg leading-10 mr-1 text-ellipsis shrink-0 cursor-pointer overflow-hidden text-gray-700 dark:text-gray-200"
|
className="font-bold text-lg leading-10 mr-1 text-ellipsis shrink-0 cursor-pointer overflow-hidden text-gray-700 dark:text-gray-200"
|
||||||
onClick={() => location.reload()}
|
onClick={() => location.reload()}
|
||||||
|
@@ -9,7 +9,9 @@ function Root() {
|
|||||||
<DemoBanner />
|
<DemoBanner />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-6xl mx-auto flex flex-row justify-center items-start sm:px-4">
|
<div className="w-full max-w-6xl mx-auto flex flex-row justify-center items-start sm:px-4">
|
||||||
<Header />
|
<div className="hidden sm:block sticky top-0 left-0 w-56">
|
||||||
|
<Header />
|
||||||
|
</div>
|
||||||
<main className="w-full min-h-screen sm:max-w-[calc(100%-14rem)] flex-grow shrink flex flex-col justify-start items-start">
|
<main className="w-full min-h-screen sm:max-w-[calc(100%-14rem)] flex-grow shrink flex flex-col justify-start items-start">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import store, { useAppSelector } from "..";
|
import store, { useAppSelector } from "..";
|
||||||
import { setHeaderStatus, setHomeSidebarStatus } from "../reducer/layout";
|
import { setHomeSidebarStatus } from "../reducer/layout";
|
||||||
|
|
||||||
export const useLayoutStore = () => {
|
export const useLayoutStore = () => {
|
||||||
const state = useAppSelector((state) => state.layout);
|
const state = useAppSelector((state) => state.layout);
|
||||||
@@ -8,9 +8,6 @@ export const useLayoutStore = () => {
|
|||||||
getState: () => {
|
getState: () => {
|
||||||
return store.getState().tag;
|
return store.getState().tag;
|
||||||
},
|
},
|
||||||
setHeaderStatus: (showHeader: boolean) => {
|
|
||||||
store.dispatch(setHeaderStatus(showHeader));
|
|
||||||
},
|
|
||||||
setHomeSidebarStatus: (showHomeSidebar: boolean) => {
|
setHomeSidebarStatus: (showHomeSidebar: boolean) => {
|
||||||
store.dispatch(setHomeSidebarStatus(showHomeSidebar));
|
store.dispatch(setHomeSidebarStatus(showHomeSidebar));
|
||||||
},
|
},
|
||||||
|
@@ -1,23 +1,15 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
showHeader: boolean;
|
|
||||||
showHomeSidebar: boolean;
|
showHomeSidebar: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const layoutSlice = createSlice({
|
const layoutSlice = createSlice({
|
||||||
name: "layout",
|
name: "layout",
|
||||||
initialState: {
|
initialState: {
|
||||||
showHeader: false,
|
|
||||||
showHomeSidebar: false,
|
showHomeSidebar: false,
|
||||||
} as State,
|
} as State,
|
||||||
reducers: {
|
reducers: {
|
||||||
setHeaderStatus: (state, action: PayloadAction<boolean>) => {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
showHeader: action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setHomeSidebarStatus: (state, action: PayloadAction<boolean>) => {
|
setHomeSidebarStatus: (state, action: PayloadAction<boolean>) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@@ -27,6 +19,6 @@ const layoutSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { setHeaderStatus, setHomeSidebarStatus } = layoutSlice.actions;
|
export const { setHomeSidebarStatus } = layoutSlice.actions;
|
||||||
|
|
||||||
export default layoutSlice.reducer;
|
export default layoutSlice.reducer;
|
||||||
|
Reference in New Issue
Block a user