From be8422d4a537ef555f988d3421f0073bf13b62ff Mon Sep 17 00:00:00 2001 From: gitlimes Date: Tue, 19 Sep 2023 18:15:35 +0200 Subject: [PATCH] feat: mobile dropdown, minor changes --- src/components/Dropdown/Dropdown.js | 52 +++++++++++------ src/components/Dropdown/Dropdown.module.css | 32 +++++++++-- src/components/Header/Header.js | 16 +++--- src/components/Header/Header.module.css | 57 ++++++++++++++++++- .../HeaderDropdowns/HeaderDropdown.module.css | 28 ++++++++- .../Header/HeaderDropdowns/HeaderDropdown1.js | 13 ++++- src/components/Title/Title.module.css | 1 + 7 files changed, 163 insertions(+), 36 deletions(-) diff --git a/src/components/Dropdown/Dropdown.js b/src/components/Dropdown/Dropdown.js index 8b252a6..fa5af36 100644 --- a/src/components/Dropdown/Dropdown.js +++ b/src/components/Dropdown/Dropdown.js @@ -10,8 +10,9 @@ import styles from './Dropdown.module.css'; * A reusable object that renders a dropdown. * * @param {React.useState} openState: a state object [state, setState], - * where state is an int which refers to the index of the dropdownContent to render + 1 - * (having it be +1 lets us use 0 as a "do not render" magic number) + * where state.e is an int which refers to the index of the dropdownContent to render + 1 + * (having it be +1 lets us use 0 as a "do not render" magic number), and c notes if the + * element was clicked (true) or hovered (falase) * * @param {Array} dropdownContents: an array of objects, structured as * follows: @@ -20,12 +21,12 @@ import styles from './Dropdown.module.css'; * the corresponding state, and tr1 is the reference to the element that triggered * the dropdown in the first place * - * @param {React.useRef} boundaryRef: a ref to the object to use as a boundary (the - * dropdown will always be rendered to be [horizontally] inside of the element) + * @param {React.useRef} boundaryRef: a ref to the object to use as a boundary (the + * dropdown will always be rendered to be [horizontally] inside of the element) * - * @param {Number} boundaryTolerance: the amount of pixels by which the dropdown can poke out of the boundary. + * @param {Number} boundaryTolerance: the amount of pixels by which the dropdown can poke out of the boundary. * - * @example + * @example * , tr: tr1 }, @@ -65,7 +66,7 @@ export default function Dropdown(ctx) { const pxToInt = (px) => parseInt(px.replace('px', '')); const clamp = (num, min, max) => Math.min(Math.max(num, min), max); - if (dropdownContentRect && open) { + if (dropdownContentRect && open.e) { const sectionWidth = pxToInt(getComputedStyle(boundaryRef.current).width); const dropdownWidth = dropdownRect.width; @@ -92,8 +93,8 @@ export default function Dropdown(ctx) { return getTriggerOffset(trRef) + getRealCenter(trRef); } - const arrowOffset = getLeftOffset(dropdownTriggers[open]); - const leftOffset = getLeftOffset(dropdownTriggers[open]) - dropdownWidth / 2; + const arrowOffset = getLeftOffset(dropdownTriggers[open.e]); + const leftOffset = getLeftOffset(dropdownTriggers[open.e]) - dropdownWidth / 2; const leftBoundary = 0 - boundaryTolerance; const rightBoundary = sectionWidth - dropdownWidth + boundaryTolerance; @@ -111,29 +112,44 @@ export default function Dropdown(ctx) { //dropdown.current.style.width = 0; dropdown.current.style.height = 0; } - }, [open, dropdownTriggers, boundaryTolerance, boundaryRef]); + + const closeOnClickOutside = (e) => { + if (dropdown.current && !dropdown.current.contains(e.target)) { + setOpen({ e: 0, c: false }); + } + }; + + document.addEventListener('mousedown', closeOnClickOutside); + return () => { + document.removeEventListener('mousedown', closeOnClickOutside); + }; + }, [open, setOpen, dropdownTriggers, boundaryTolerance, boundaryRef]); return ( <>
setOpen(open)} - onPointerEnter={() => setOpen(open)} - onMouseLeave={() => setOpen(0)} - onPointerLeave={() => setOpen(0)} + onMouseEnter={() => setOpen({ e: open.e, c: open.c })} + onPointerEnter={() => setOpen({ e: open.e, c: open.c })} + onMouseLeave={() => { + open.c ? null : setOpen({ e: 0, c: false }); + }} + onPointerLeave={() => (open.c ? null : setOpen({ e: 0, c: false }))} + onMouseDown={() => setTimeout(() => setOpen({ e: 0, c: false }), 200)} + onPointerDown={() => setTimeout(() => setOpen({ e: 0, c: false }), 200)} ref={dropdown} style={{ - border: open ? '' : 'none', + border: open.e ? '' : 'none', }} >
- {dropdownElements[open]} + {dropdownElements[open.e]}
diff --git a/src/components/Dropdown/Dropdown.module.css b/src/components/Dropdown/Dropdown.module.css index 362a26d..905c907 100644 --- a/src/components/Dropdown/Dropdown.module.css +++ b/src/components/Dropdown/Dropdown.module.css @@ -27,12 +27,12 @@ .dropdownArrow.arrowAppear { transform: scale(1) translateX(-50%) rotate(45deg); - transition: transform 250ms ease-out 200ms; + transition: transform 250ms ease-out 200ms, left ease-out 200ms; } .dropdownArrow.arrowDisappear { transform: scale(0) translateX(-50%) rotate(45deg); - transition: transform 50ms ease-in; + transition: transform 50ms ease-in, left ease-out 200ms; } .dropdown::before { @@ -48,7 +48,7 @@ .dropdown .dropdownContent { width: max-content; height: max-content; - padding: 1.25rem; + padding: 1rem; } .dropdownContent .link { @@ -57,7 +57,7 @@ flex-flow: row nowrap; align-items: center; gap: 1rem; - padding: 1.25rem; + padding: 1rem; padding-right: 5rem; border-radius: 6px; max-width: 90vw; @@ -113,3 +113,27 @@ .dropdownContent .link:hover .arrow { display: block; } + +@media screen and (max-width: 840px) { + .dropdown { + position: fixed; + bottom: 0 !important; + left: 5% !important; + width: 90% !important; + z-index: -1; + } + + .dropdownArrow { + display: none; + } + + .dropdown .dropdownContent { + width: 100%; + } +} + +@media screen and (max-width: 630px) { + .dropdown .dropdownContent { + padding: 0; + } +} diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 1cdfdda..80c0546 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -22,7 +22,7 @@ import HeaderDropdown3 from './HeaderDropdowns/HeaderDropdown3'; import HeaderDropdown4 from './HeaderDropdowns/HeaderDropdown4'; export default function Header({ locale }) { - const [open, setOpen] = useState(0); + const [open, setOpen] = useState({ e: 0, c: false }); const headerInnerSection = useRef(null); @@ -35,12 +35,14 @@ export default function Header({ locale }) { return ( diff --git a/src/components/Header/Header.module.css b/src/components/Header/Header.module.css index 2617935..8141886 100644 --- a/src/components/Header/Header.module.css +++ b/src/components/Header/Header.module.css @@ -28,6 +28,7 @@ width: 100%; justify-content: space-between; align-items: center; + gap: 2rem; } .nav { @@ -52,11 +53,13 @@ gap: 0; } -.nav .dropdownTriggers li:first-of-type button { +.nav .dropdownTriggers li:first-of-type button, +.rightIconsWrapper button:first-of-type { padding-right: 1rem; } -.nav .dropdownTriggers li:last-of-type button { +.nav .dropdownTriggers li:last-of-type button, +.rightIconsWrapper button:last-of-type { padding-left: 1rem; } @@ -97,5 +100,53 @@ display: flex; flex-flow: row nowrap; align-items: center; - gap: 1rem; +} + +@media screen and (max-width: 840px) { + .progress { + display: none; + } +} + +@media screen and (max-width: 630px) { + .nav li.upgrade button { + padding: 6px; + } + .nav li.upgrade span { + display: none; + } + + .logo { + margin-right: -104px; + } + + .logo text { + display: none; + } + + .nav { + width: 100%; + } + .dropdownTriggers { + width: 100%; + } + + .navWrapper { + gap: 1rem; + } + + .nav, + .nav ul { + gap: 1rem; + } + + .nav .dropdownTriggers li:first-of-type button, + .rightIconsWrapper button:first-of-type { + padding-right: 0.5rem; + } + + .nav .dropdownTriggers li:last-of-type button, + .rightIconsWrapper button:last-of-type { + padding-left: 0.5rem; + } } diff --git a/src/components/Header/HeaderDropdowns/HeaderDropdown.module.css b/src/components/Header/HeaderDropdowns/HeaderDropdown.module.css index 4f47816..fc44897 100644 --- a/src/components/Header/HeaderDropdowns/HeaderDropdown.module.css +++ b/src/components/Header/HeaderDropdowns/HeaderDropdown.module.css @@ -4,7 +4,7 @@ flex-flow: row nowrap; align-items: center; gap: 1rem; - padding: 1.25rem; + padding: 1rem; padding-right: 5rem; border-radius: 6px; max-width: 90vw; @@ -25,7 +25,7 @@ margin: 0; margin-top: 4px; font-weight: 300; - color: var(--text-shade-3); + color: var(--text-shade-1); } .dropdownContent .link .arrow { @@ -40,6 +40,10 @@ animation: appear 0.75s; } +.link.progress { + display: none; +} + @keyframes appear { 0% { opacity: 0; @@ -54,9 +58,27 @@ } .dropdownContent .link:hover .iconWrapper { - color: var(--accent-shade-1); + color: var(--accent-shade-3); } .dropdownContent .link:hover .arrow { display: block; } + +@media screen and (max-width: 840px) { + .link.progress { + display: flex; + } +} + +@media screen and (max-width: 630px) { + .dropdownContent .link:hover .arrow { + display: none; + } +} + +@media screen and (max-width: 360px) { + .dropdownContent .link .iconWrapper { + padding: 6px; + } +} diff --git a/src/components/Header/HeaderDropdowns/HeaderDropdown1.js b/src/components/Header/HeaderDropdowns/HeaderDropdown1.js index 5caafdb..a6e523e 100644 --- a/src/components/Header/HeaderDropdowns/HeaderDropdown1.js +++ b/src/components/Header/HeaderDropdowns/HeaderDropdown1.js @@ -1,4 +1,4 @@ -import { ArrowRight, Users, Info, SealQuestion, NewspaperClipping } from '@phosphor-icons/react'; +import { ArrowRight, Users, Info, SealQuestion, NewspaperClipping, EggCrack } from '@phosphor-icons/react'; import Link from 'next/link'; import Title from '@/components/Title/Title'; @@ -51,6 +51,17 @@ export default function HeaderDropdown1({ locale }) {
+ + +
+ +
+
+ {locale.nav.progress} +

{locale.nav.dropdown.captions.progress}

+
+ + ); } diff --git a/src/components/Title/Title.module.css b/src/components/Title/Title.module.css index ff2c473..9abb24d 100644 --- a/src/components/Title/Title.module.css +++ b/src/components/Title/Title.module.css @@ -2,6 +2,7 @@ font-weight: 700; margin: 0; color: var(--text-shade-3); + scroll-margin-top: 90px; } .center {