From 0c148f0e925a1eb58a7415957254c52432785fb1 Mon Sep 17 00:00:00 2001 From: Victoria Fedkova <63882255+Victoria-Fedkova@users.noreply.github.com> Date: Thu, 21 Sep 2023 04:00:42 +0300 Subject: [PATCH] add mobile sidebar --- package-lock.json | 41 +++++++++++++++++++ package.json | 1 + src/components/CarCard/CarCard.styled.jsx | 2 +- .../PageFooter/PageFooter.styled.jsx | 1 + src/components/PageHeader/PageHeader.jsx | 23 +++++++++-- .../PageHeader/PageHeader.styled.jsx | 20 ++++++++- src/components/SharedLayout/SharedLayout.jsx | 18 ++++++-- src/components/SideBar/SideBar.jsx | 15 ++++++- src/components/SideBar/SideBar.styled.jsx | 13 ++++++ src/pages/NotFound/NotFound.jsx | 9 ++-- src/redux/sidebar/sidebarSelectors.js | 1 + src/redux/sidebar/sidebarSlice.js | 18 ++++++++ src/redux/store.js | 2 + src/styles/globalStyles.jsx | 1 + 14 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 src/redux/sidebar/sidebarSelectors.js create mode 100644 src/redux/sidebar/sidebarSlice.js diff --git a/package-lock.json b/package-lock.json index ec36b63..3890e6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "react-input-slider": "^6.0.1", "react-number-format": "^5.3.1", "react-redux": "^8.1.2", + "react-responsive": "^9.0.2", "react-router-dom": "^6.16.0", "react-select": "^5.7.4", "react-slick": "^0.29.0", @@ -3504,6 +3505,11 @@ "node": ">=4" } }, + "node_modules/css-mediaquery": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/css-mediaquery/-/css-mediaquery-0.1.2.tgz", + "integrity": "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==" + }, "node_modules/css-to-react-native": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", @@ -4510,6 +4516,11 @@ "react-is": "^16.7.0" } }, + "node_modules/hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -5136,6 +5147,14 @@ "semver": "bin/semver" } }, + "node_modules/matchmediaquery": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/matchmediaquery/-/matchmediaquery-0.3.1.tgz", + "integrity": "sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ==", + "dependencies": { + "css-mediaquery": "^0.1.2" + } + }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -5697,6 +5716,23 @@ "node": ">=0.10.0" } }, + "node_modules/react-responsive": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-9.0.2.tgz", + "integrity": "sha512-+4CCab7z8G8glgJoRjAwocsgsv6VA2w7JPxFWHRc7kvz8mec1/K5LutNC2MG28Mn8mu6+bu04XZxHv5gyfT7xQ==", + "dependencies": { + "hyphenate-style-name": "^1.0.0", + "matchmediaquery": "^0.3.0", + "prop-types": "^15.6.1", + "shallow-equal": "^1.2.1" + }, + "engines": { + "node": ">=0.10" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-router": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz", @@ -6076,6 +6112,11 @@ "node": ">= 0.4" } }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", diff --git a/package.json b/package.json index 99aa111..6de641f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "react-input-slider": "^6.0.1", "react-number-format": "^5.3.1", "react-redux": "^8.1.2", + "react-responsive": "^9.0.2", "react-router-dom": "^6.16.0", "react-select": "^5.7.4", "react-slick": "^0.29.0", diff --git a/src/components/CarCard/CarCard.styled.jsx b/src/components/CarCard/CarCard.styled.jsx index 61dcf2b..5f832eb 100644 --- a/src/components/CarCard/CarCard.styled.jsx +++ b/src/components/CarCard/CarCard.styled.jsx @@ -42,7 +42,7 @@ export const ImgWrapper = styled.div` right: 14px; border: none; background: transparent; - z-index: 1000; + z-index: 2; padding: 0; margin: 0; transition: all 250ms linear; diff --git a/src/components/PageFooter/PageFooter.styled.jsx b/src/components/PageFooter/PageFooter.styled.jsx index 45acbc5..18fb291 100644 --- a/src/components/PageFooter/PageFooter.styled.jsx +++ b/src/components/PageFooter/PageFooter.styled.jsx @@ -13,6 +13,7 @@ export const FooterTopWrapper = styled.div` padding: 34px 0; display: flex; flex-direction: column; + align-items: center; gap: 1rem; @media (min-width: 768px) { diff --git a/src/components/PageHeader/PageHeader.jsx b/src/components/PageHeader/PageHeader.jsx index 9fea460..e71e251 100644 --- a/src/components/PageHeader/PageHeader.jsx +++ b/src/components/PageHeader/PageHeader.jsx @@ -1,12 +1,20 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faHeart, faCar, faHome } from '@fortawesome/free-solid-svg-icons'; +import { + faHeart, + faCar, + faHome, + faBookOpen, + faClose, +} from '@fortawesome/free-solid-svg-icons'; import { Logo } from '../Logo/Logo'; -import { Header, HeaderWraper, NavLinks } from './PageHeader.styled'; +import { Header, HeaderWraper, NavLinks, SideBtn } from './PageHeader.styled'; import { useDispatch } from 'react-redux'; import { setFilter } from '../../redux/filter/filterSlice'; +import { useMediaQuery } from 'react-responsive'; -export const PageHeader = () => { +export const PageHeader = ({ currentState, handleToggleSideBar }) => { const dispatch = useDispatch(); + const isMobile = useMediaQuery({ query: '(max-width: 1279px)' }); return (
@@ -43,6 +51,15 @@ export const PageHeader = () => { }} /> + {isMobile && ( + + {currentState ? ( + + ) : ( + + )} + + )}
diff --git a/src/components/PageHeader/PageHeader.styled.jsx b/src/components/PageHeader/PageHeader.styled.jsx index 4bb307a..4ecfa34 100644 --- a/src/components/PageHeader/PageHeader.styled.jsx +++ b/src/components/PageHeader/PageHeader.styled.jsx @@ -45,7 +45,25 @@ export const NavLinks = styled(NavLink)` &:focus { background-color: #3470ff; color: white; - border-color: #3470ff; } } `; + +export const SideBtn = styled.button` + transition: all 250ms linear; + display: inline-flex; + width: 44px; + height: 44px; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 1px solid rgba(195, 212, 233, 0.4); + color: rgba(18, 20, 23, 0.5); + fill: rgba(18, 20, 23, 0.5); + background-color: white; + &:hover, + &:focus { + background-color: #3470ff; + color: white; + } +`; diff --git a/src/components/SharedLayout/SharedLayout.jsx b/src/components/SharedLayout/SharedLayout.jsx index 00a1bd5..4c0785f 100644 --- a/src/components/SharedLayout/SharedLayout.jsx +++ b/src/components/SharedLayout/SharedLayout.jsx @@ -2,11 +2,13 @@ import { Outlet, useLocation } from 'react-router-dom'; import { Suspense, useEffect } from 'react'; import { PageHeader } from '../PageHeader/PageHeader'; import { PageFooter } from '../PageFooter/PageFooter'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { fetchCars } from '../../redux/cars/carsOperations'; import { SideBar } from '../SideBar/SideBar'; import { ScrollToTop } from '../ScrollToTop/ScrollToTop'; import { Main } from './SharedLayout.styled'; +import { toggleSideBar } from '../../redux/sidebar/sidebarSlice'; +import { selectOpenSideBar } from '../../redux/sidebar/sidebarSelectors'; export const SharedLayout = () => { const { pathname } = useLocation(); @@ -15,11 +17,21 @@ export const SharedLayout = () => { useEffect(() => { dispatch(fetchCars()); }, [dispatch]); + + const currentState = useSelector(selectOpenSideBar); + + const handleToggleSideBar = () => { + dispatch(toggleSideBar()); + }; + return ( <> - +
- {isFavouritePage && } + {isFavouritePage && } Loading...}> diff --git a/src/components/SideBar/SideBar.jsx b/src/components/SideBar/SideBar.jsx index 22ec3c1..42f332c 100644 --- a/src/components/SideBar/SideBar.jsx +++ b/src/components/SideBar/SideBar.jsx @@ -1,9 +1,20 @@ +import { useEffect } from 'react'; import { SideBarWrapper } from './SideBar.styled'; import { SideBarSlider } from './SideBarForm/SIdeBarSlider'; import { SideBarForm } from './SideBarForm/SideBarForm'; -export const SideBar = () => { + +export const SideBar = ({ currentState }) => { + useEffect(() => { + // Applying on mount + if (currentState) document.body.style.overflow = 'hidden'; + // Applying on unmount + return () => { + document.body.style.overflow = 'visible'; + }; + }, [currentState]); + return ( - + diff --git a/src/components/SideBar/SideBar.styled.jsx b/src/components/SideBar/SideBar.styled.jsx index a0719a3..a35416c 100644 --- a/src/components/SideBar/SideBar.styled.jsx +++ b/src/components/SideBar/SideBar.styled.jsx @@ -5,6 +5,19 @@ export const SideBarWrapper = styled.div` background-color: white; min-height: 100%; padding: 32px; + @media screen and (max-width: 1280px) { + position: fixed; + z-index: 999; + height: 100%; + transform: ${props => + props.$isOpen ? 'transform: translateX(0%)' : 'translateX(-100%)'}; + transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1); + } + + body & { + overflow: ${props => (props.$isOpen ? 'hidden' : 'auto')}; + overflow: hidden; + } `; export const CheckboxForm = styled.form` diff --git a/src/pages/NotFound/NotFound.jsx b/src/pages/NotFound/NotFound.jsx index e4b502c..43296be 100644 --- a/src/pages/NotFound/NotFound.jsx +++ b/src/pages/NotFound/NotFound.jsx @@ -1,7 +1,10 @@ +import cars from '../../assets/pngegg.png'; + export default function NotFound() { return ( - <> -

NotFound

- +
+

404 Page not found

+ Page not found +
); } diff --git a/src/redux/sidebar/sidebarSelectors.js b/src/redux/sidebar/sidebarSelectors.js new file mode 100644 index 0000000..26c2d3d --- /dev/null +++ b/src/redux/sidebar/sidebarSelectors.js @@ -0,0 +1 @@ +export const selectOpenSideBar = state => state.sideBar.currentState; diff --git a/src/redux/sidebar/sidebarSlice.js b/src/redux/sidebar/sidebarSlice.js new file mode 100644 index 0000000..350be3a --- /dev/null +++ b/src/redux/sidebar/sidebarSlice.js @@ -0,0 +1,18 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + currentState: true, +}; + +const sideBarSlice = createSlice({ + name: 'sideBar', + initialState, + reducers: { + toggleSideBar: state => { + state.currentState = !state.currentState; + }, + }, +}); + +export const { toggleSideBar } = sideBarSlice.actions; +export const sideBarReducer = sideBarSlice.reducer; diff --git a/src/redux/store.js b/src/redux/store.js index 8530724..de70c64 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -15,6 +15,7 @@ import storage from 'redux-persist/lib/storage'; import { carsReducer } from './cars/carsSlice'; import { filterReducer } from './filter/filterSlice'; import { likesReducer } from './likes/likesSlice'; +import { sideBarReducer } from './sidebar/sidebarSlice'; const middleware = [ ...getDefaultMiddleware({ @@ -35,6 +36,7 @@ export const store = configureStore({ cars: carsReducer, filter: filterReducer, likes: persistReducer(likesPersistConfig, likesReducer), + sideBar: sideBarReducer, }, middleware, }); diff --git a/src/styles/globalStyles.jsx b/src/styles/globalStyles.jsx index eb67be8..8b5ecd9 100644 --- a/src/styles/globalStyles.jsx +++ b/src/styles/globalStyles.jsx @@ -6,6 +6,7 @@ import ManropeSemiBold from '../fonts/Manrope-SemiBold.ttf'; const GlobalStyle = createGlobalStyle` + @font-face { font-family: 'ManropeRegular'; src: local('ManropeRegular'),