From 2c7ec344630406563faf45c65fe9ed5fd5508f09 Mon Sep 17 00:00:00 2001 From: gitlimes Date: Sun, 1 Oct 2023 14:36:11 +0200 Subject: [PATCH] feat: improve state management, fix bug... ...where the header buttons would just rerender on every mouse *move*, which, yanno, is not great. --- src/components/Dropdown/Dropdown.js | 50 ++++++++++++--------- src/components/Dropdown/Dropdown.module.css | 4 +- src/components/Header/Header.js | 22 +++++---- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/components/Dropdown/Dropdown.js b/src/components/Dropdown/Dropdown.js index fa5af36..c375ae6 100644 --- a/src/components/Dropdown/Dropdown.js +++ b/src/components/Dropdown/Dropdown.js @@ -9,10 +9,13 @@ import styles from './Dropdown.module.css'; * * A reusable object that renders a dropdown. * - * @param {React.useState} openState: a state object [state, setState], - * 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 {React.useState} openState: a state object [state, setState] storing 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) + * + * + * @param {React.useState} clickedState: a state object [state, setState] storing a + * boolean indicating whether the element was clicked on (true) or hovered on (false) * * @param {Array} dropdownContents: an array of objects, structured as * follows: @@ -33,6 +36,7 @@ import styles from './Dropdown.module.css'; * { el: , tr: tr2 }, * ]} * openState={[open, setOpen]} + * clickedState={[clicked, setClicked]} * boundaryRef={navbarRef} * boundaryTolerance={20} * /> @@ -40,6 +44,7 @@ import styles from './Dropdown.module.css'; export default function Dropdown(ctx) { const [open, setOpen] = ctx.openState; + const [clicked, setClicked] = ctx.clickedState; const { boundaryRef, boundaryTolerance } = ctx; const dropdownElements = useMemo(() => { @@ -66,7 +71,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.e) { + if (dropdownContentRect && open) { const sectionWidth = pxToInt(getComputedStyle(boundaryRef.current).width); const dropdownWidth = dropdownRect.width; @@ -93,8 +98,8 @@ export default function Dropdown(ctx) { return getTriggerOffset(trRef) + getRealCenter(trRef); } - const arrowOffset = getLeftOffset(dropdownTriggers[open.e]); - const leftOffset = getLeftOffset(dropdownTriggers[open.e]) - dropdownWidth / 2; + const arrowOffset = getLeftOffset(dropdownTriggers[open]); + const leftOffset = getLeftOffset(dropdownTriggers[open]) - dropdownWidth / 2; const leftBoundary = 0 - boundaryTolerance; const rightBoundary = sectionWidth - dropdownWidth + boundaryTolerance; @@ -115,7 +120,8 @@ export default function Dropdown(ctx) { const closeOnClickOutside = (e) => { if (dropdown.current && !dropdown.current.contains(e.target)) { - setOpen({ e: 0, c: false }); + setOpen(0); + setClicked(false); } }; @@ -123,33 +129,37 @@ export default function Dropdown(ctx) { return () => { document.removeEventListener('mousedown', closeOnClickOutside); }; - }, [open, setOpen, dropdownTriggers, boundaryTolerance, boundaryRef]); + }, [open, setOpen, clicked, setClicked, dropdownTriggers, boundaryTolerance, boundaryRef]); return ( <>
setOpen({ e: open.e, c: open.c })} - onPointerEnter={() => setOpen({ e: open.e, c: open.c })} - onMouseLeave={() => { - open.c ? null : setOpen({ e: 0, c: false }); + onPointerEnter={() => { + setOpen(open); + }} + onPointerLeave={() => { + !clicked && setOpen(0); + }} + onPointerDown={() => { + setTimeout(() => { + setOpen(0); + setClicked(false); + }, 200); }} - 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.e ? '' : 'none', + border: open ? '' : 'none', }} >
- {dropdownElements[open.e]} + {dropdownElements[open]}
diff --git a/src/components/Dropdown/Dropdown.module.css b/src/components/Dropdown/Dropdown.module.css index 905c907..5b7cfe9 100644 --- a/src/components/Dropdown/Dropdown.module.css +++ b/src/components/Dropdown/Dropdown.module.css @@ -39,8 +39,8 @@ content: ''; position: absolute; display: block; - height: 1.4rem; - top: -1.4rem; + height: 1.6rem; + top: -1.6rem; left: 0; width: 100%; } diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js index 80c0546..c2029da 100644 --- a/src/components/Header/Header.js +++ b/src/components/Header/Header.js @@ -22,7 +22,8 @@ import HeaderDropdown3 from './HeaderDropdowns/HeaderDropdown3'; import HeaderDropdown4 from './HeaderDropdowns/HeaderDropdown4'; export default function Header({ locale }) { - const [open, setOpen] = useState({ e: 0, c: false }); + const [open, setOpen] = useState(0); + const [clicked, setClicked] = useState(false); const headerInnerSection = useRef(null); @@ -35,14 +36,18 @@ export default function Header({ locale }) { return (