Fix menu list on mobile

* add ahooks library
* add useAppDispatch and useAppSelector hooks
* move out router event handle from useEffect
* fix router events off
This commit is contained in:
DefectingCat
2021-11-25 15:54:35 +08:00
parent b17c365063
commit 4db3db2a24
8 changed files with 144 additions and 70 deletions

6
app/hooks.ts Normal file
View File

@ -0,0 +1,6 @@
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

View File

@ -1,4 +1,4 @@
import { FC, MouseEvent, useEffect } from 'react';
import { FC, MouseEvent, useRef } from 'react';
import {
Box,
Image,
@ -13,9 +13,8 @@ import {
Button,
} from '@chakra-ui/react';
import UseAnimations from 'react-useanimations';
import menu2 from 'react-useanimations/lib/menu2';
import menu3 from 'react-useanimations/lib/menu3';
import { FiHome, FiArchive, FiUser, FiSun, FiMoon } from 'react-icons/fi';
import Link from 'next/link';
import useGetColors from '../lib/hooks/useGetColors';
import { useRouter } from 'next/router';
@ -40,28 +39,68 @@ const menu = [
},
];
interface MenuListProps {
boxBg: string;
handleMenuClick: (
// eslint-disable-next-line no-unused-vars
e: MouseEvent<HTMLDivElement, globalThis.MouseEvent>,
// eslint-disable-next-line no-unused-vars
path: string
) => void;
}
const MenuList: FC<MenuListProps> = ({ boxBg, handleMenuClick }) => {
return (
<Flex
as="nav"
mt={['1.5rem', null]}
flexFlow={['column']}
py={['1rem', null, 'unset']}
px={['2rem', '4rem', 'unset']}
bg={[boxBg, null, 'unset']}
shadow={['card', null, 'unset']}
>
{menu.map((item) => {
return (
<Flex
onClick={(e) => handleMenuClick(e, item.path)}
href={item.path}
key={item.id}
as="a"
alignItems="center"
justifyContent={['unset', null, 'space-between']}
fontSize={['18', null, '22']}
my={['0.5rem', null, '0.5rem']}
cursor="pointer"
>
<Icon as={item.icon} mr={['2.5rem', null, 'unset']} />
<Text>{item.name}</Text>
</Flex>
);
})}
</Flex>
);
};
const NavBar: FC = () => {
const router = useRouter();
const [isLargerThan768] = useMediaQuery('(min-width: 768px)');
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: false });
const { colorMode, toggleColorMode } = useColorMode();
const { boxBg, bioColor, textColor, headingColor } = useGetColors();
useEffect(() => {
if ((isOpen && !isLargerThan768) || (!isOpen && isLargerThan768))
onToggle();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLargerThan768]);
const iconRef = useRef<HTMLDivElement>(null);
// Switch pages
const handleMenuClick = (
e: MouseEvent<HTMLDivElement, globalThis.MouseEvent>,
path: string
) => {
e.preventDefault();
!isLargerThan768 && onToggle();
!isLargerThan768 &&
// Click the animate icon.
(iconRef.current?.children[0] as HTMLDivElement).click();
router.push(path);
};
@ -124,36 +163,19 @@ const NavBar: FC = () => {
{/* Menu */}
<Box mx={['-2rem', '-4rem', 'unset']}>
<Collapse in={isOpen} animateOpacity>
<Flex
as="nav"
mt={['1.5rem', null]}
flexFlow={['column']}
py={['1rem', null, 'unset']}
px={['2rem', '4rem', 'unset']}
bg={[boxBg, null, 'unset']}
shadow={['card', null, 'unset']}
>
{menu.map((item) => {
return (
<Flex
onClick={(e) => handleMenuClick(e, item.path)}
href={item.path}
key={item.id}
as="a"
alignItems="center"
justifyContent={['unset', null, 'space-between']}
fontSize={['18', null, '22']}
my={['0.5rem', null, '0.5rem']}
cursor="pointer"
>
<Icon as={item.icon} mr={['2.5rem', null, 'unset']} />
<Text>{item.name}</Text>
</Flex>
);
})}
</Flex>
</Collapse>
{/* Mobile menu */}
<Box
as={Collapse}
in={isOpen}
animateOpacity
display={[null, null, 'none']}
>
<MenuList boxBg={boxBg} handleMenuClick={handleMenuClick} />
</Box>
{/* Desktop menu */}
<Box display={['none', 'none', 'block']}>
<MenuList boxBg={boxBg} handleMenuClick={handleMenuClick} />
</Box>
</Box>
<Box
@ -161,13 +183,14 @@ const NavBar: FC = () => {
position={'absolute'}
top={'1.5rem'}
right={'1rem'}
onClick={onToggle}
ref={iconRef}
>
<UseAnimations
reverse={isOpen}
size={40}
animation={menu2}
speed={1.5}
animation={menu3}
speed={2}
onClick={onToggle}
/>
</Box>
</Box>

View File

@ -4,7 +4,7 @@ import { Box, Flex, Heading, Text, Link } from '@chakra-ui/react';
import { AllPostsData } from '../lib/posts';
import { Icon, Image } from '@chakra-ui/react';
import { FiCalendar, FiTag } from 'react-icons/fi';
import { useDispatch } from 'react-redux';
import { useAppDispatch } from '../app/hooks';
import { setFromPath } from '../features/router/routerSlice';
import { useRouter } from 'next/router';
import useGetColors from '../lib/hooks/useGetColors';
@ -14,7 +14,7 @@ interface Props {
}
const PostCard: FC<Props> = ({ post }) => {
const dispatch = useDispatch();
const dispatch = useAppDispatch();
const router = useRouter();
const goToPost: MouseEventHandler<HTMLAnchorElement> = (e) => {

View File

@ -5,6 +5,7 @@ export interface RouterState {
}
const initialState: RouterState = {
// Record the from path when into the post page.
fromPath: '',
};

View File

@ -20,6 +20,7 @@
"@emotion/styled": "^11",
"@giscus/react": "^1.0.1",
"@reduxjs/toolkit": "^1.6.2",
"ahooks": "^2.10.12",
"autoprefixer": "^10.3.7",
"date-fns": "^2.25.0",
"framer-motion": "^5.0.2",

View File

@ -26,24 +26,23 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const router = useRouter();
const handleRouteChange = (
url: string,
{ shallow }: { shallow: 'with' | 'without' }
) => {
console.log(
`App is changing to ${url} ${
shallow ? 'with' : 'without'
} shallow routing`
);
};
const handleStart = () => {
NProgress.start();
};
const handleStop = () => {
NProgress.done();
};
useEffect(() => {
const handleRouteChange = (
url: string,
{ shallow }: { shallow: 'with' | 'without' }
) => {
console.log(
`App is changing to ${url} ${
shallow ? 'with' : 'without'
} shallow routing`
);
};
const handleStart = () => {
NProgress.start();
};
const handleStop = () => {
NProgress.done();
};
router.events.on('routeChangeStart', handleRouteChange);
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeStart', handleStop);
@ -52,8 +51,8 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// from the event with the `off` method:
return () => {
router.events.off('routeChangeStart', handleRouteChange);
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeStart', handleStop);
router.events.off('routeChangeStart', handleStart);
router.events.off('routeChangeStart', handleStop);
};
}, [router.events]);

View File

@ -33,11 +33,11 @@ import { useRouter } from 'next/router';
import Footer from '../../components/Footer';
import { Giscus } from '@giscus/react';
import { RootState } from '../../app/store';
import { useSelector, useDispatch } from 'react-redux';
import { cleanFromPath } from '../../features/router/routerSlice';
import CopyButton from '../../components/post/CopyButton';
import useGetColors from '../../lib/hooks/useGetColors';
import PostImage from '../../components/post/PostImage';
import { useAppSelector, useAppDispatch } from '../../app/hooks';
export async function getStaticPaths() {
const paths = await getAllPostSlugs();
@ -57,8 +57,8 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
};
const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
const fromPath = useSelector((state: RootState) => state.router.fromPath);
const dispatch = useDispatch();
const fromPath = useAppSelector((state: RootState) => state.router.fromPath);
const dispatch = useAppDispatch();
const router = useRouter();
const goBack = () => {

View File

@ -2,6 +2,14 @@
# yarn lockfile v1
"@ahooksjs/use-request@^2.8.13":
version "2.8.13"
resolved "https://registry.npmjs.org/@ahooksjs/use-request/-/use-request-2.8.13.tgz#5ace53859feb6b4fe9ebcbf2e72982bb9b7db383"
integrity sha512-zGSOwGNy6fTudvBcLJ8Ly7DwacEGzQS06fGe1b1mEPWTsK/R6y8ItIbtE2RXTeMoCpbxagamMWo5ZDJkJlmZ6Q==
dependencies:
lodash.debounce "^4.0.8"
lodash.throttle "^4.1.1"
"@babel/code-frame@7.12.11":
version "7.12.11"
resolved "https://registry.npmmirror.com/@babel/code-frame/download/@babel/code-frame-7.12.11.tgz?cache=0&sync_timestamp=1633554562995&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcode-frame%2Fdownload%2F%40babel%2Fcode-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
@ -1290,6 +1298,22 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"
ahooks@^2.10.12:
version "2.10.12"
resolved "https://registry.npmjs.org/ahooks/-/ahooks-2.10.12.tgz#e8cab653039434279e69569a8342c602b3545dab"
integrity sha512-X3nZwr6+CSi1XjGyd/tQU1vF11mNaP4Bo0pdEdUL2ksBURQl0QZvDeUMrKBYHAStq7852P20ycVgu405XxDlFg==
dependencies:
"@ahooksjs/use-request" "^2.8.13"
"@types/js-cookie" "^2.2.6"
dayjs "^1.9.1"
intersection-observer "^0.7.0"
js-cookie "^2.2.1"
lodash.debounce "^4.0.8"
lodash.isequal "^4.5.0"
lodash.throttle "^4.1.1"
resize-observer-polyfill "^1.5.1"
screenfull "^5.0.0"
ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.nlark.com/ajv/download/ajv-6.12.6.tgz?cache=0&sync_timestamp=1631470912358&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fajv%2Fdownload%2Fajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@ -2187,7 +2211,7 @@ date-fns@^2.25.0:
resolved "https://registry.npmmirror.com/date-fns/download/date-fns-2.25.0.tgz?cache=0&sync_timestamp=1633421929609&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fdate-fns%2Fdownload%2Fdate-fns-2.25.0.tgz#8c5c8f1d958be3809a9a03f4b742eba894fc5680"
integrity sha1-jFyPHZWL44CamgP0t0LrqJT8VoA=
dayjs@^1.10.4:
dayjs@^1.10.4, dayjs@^1.9.1:
version "1.10.7"
resolved "https://registry.nlark.com/dayjs/download/dayjs-1.10.7.tgz?cache=0&sync_timestamp=1631266651008&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fdayjs%2Fdownload%2Fdayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468"
integrity sha1-LPX5Gt0oEWdIRAhmoKHSbzps5Gg=
@ -3532,6 +3556,11 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
intersection-observer@^0.7.0:
version "0.7.0"
resolved "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz#ee16bee978db53516ead2f0a8154b09b400bbdc9"
integrity sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg==
invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.nlark.com/invariant/download/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@ -3965,6 +3994,16 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.npm.taobao.org/lodash.merge/download/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@ -3985,6 +4024,11 @@ lodash.sortby@^4.7.0:
resolved "https://registry.npm.taobao.org/lodash.sortby/download/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.nlark.com/lodash.truncate/download/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@ -5682,7 +5726,7 @@ scheduler@^0.20.2:
loose-envify "^1.1.0"
object-assign "^4.1.1"
screenfull@^5.1.0:
screenfull@^5.0.0, screenfull@^5.1.0:
version "5.2.0"
resolved "https://registry.npmmirror.com/screenfull/download/screenfull-5.2.0.tgz?cache=0&sync_timestamp=1635923453416&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fscreenfull%2Fdownload%2Fscreenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
integrity sha1-ZTPVJNMGIfwSg7lpIUbz8TqT0bo=