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
+
+
);
}
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'),