Skip to content

Commit

Permalink
Refacto on outside click hook
Browse files Browse the repository at this point in the history
  • Loading branch information
pookmish committed Mar 4, 2024
1 parent f53205d commit 41b1226
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 43 deletions.
22 changes: 11 additions & 11 deletions src/components/menu/main-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import {MenuItem as MenuItemType} from "@lib/gql/__generated__/drupal.d";

const MainMenu = ({menuItems}: { menuItems: MenuItemType[] }) => {
const buttonRef = useRef<HTMLButtonElement>(null)
const menuRef = useRef<HTMLDivElement>(null);

const {value: menuOpen, setFalse: closeMenu, toggle: toggleMenu} = useBoolean(false)
const browserUrl = useNavigationEvent()
const activeTrail = useActiveTrail(menuItems, usePathname() || '');

const clickProps = useOutsideClick(closeMenu);
useOutsideClick(menuRef, closeMenu);

const handleEscape = useCallback((event: KeyboardEvent) => {
if (event.key === "Escape" && menuOpen) {
Expand All @@ -32,7 +34,7 @@ const MainMenu = ({menuItems}: { menuItems: MenuItemType[] }) => {
useEventListener("keydown", handleEscape);

return (
<nav aria-label="Main Navigation" className="lg:centered" {...clickProps}>
<nav aria-label="Main Navigation" className="lg:centered" ref={menuRef}>
<button
ref={buttonRef}
className="flex flex-col items-center lg:hidden absolute top-5 right-10 group"
Expand All @@ -47,8 +49,7 @@ const MainMenu = ({menuItems}: { menuItems: MenuItemType[] }) => {
<div
className={(menuOpen ? "block" : "hidden") + " lg:block bg-black lg:bg-transparent absolute top-100 lg:relative z-10 w-full"}>
<SiteSearchForm className="px-10 lg:hidden"/>
<ul
className="list-unstyled lg:flex lg:justify-end flex-wrap m-0 p-0">
<ul className="list-unstyled lg:flex lg:justify-end flex-wrap m-0 p-0">
{menuItems.map(item =>
<MenuItem key={item.id} {...item} activeTrail={activeTrail} level={0}/>
)}
Expand All @@ -70,7 +71,7 @@ const MenuItem = ({id, url, title, activeTrail, children, level}: MenuItemProps)
const {value: submenuOpen, setFalse: closeSubmenu, toggle: toggleSubmenu} = useBoolean(false)
const browserUrl = useNavigationEvent()

const itemProps = useOutsideClick(closeSubmenu);
useOutsideClick(sublistRef, closeSubmenu);

// Close the submenu if the url changes.
useEffect(() => closeSubmenu(), [browserUrl, closeSubmenu]);
Expand All @@ -91,7 +92,7 @@ const MenuItem = ({id, url, title, activeTrail, children, level}: MenuItemProps)

useEventListener("keydown", handleEscape)

// List out the specific classes so tailwind will include them. Dynamic classes values don't get compiles well.
// List out the specific classes so tailwind will include them. Dynamic classes values don't get compiled.
const zIndexes = ["z-[1]", "z-[2]", "z-[3]", "z-[4]", "z-[5]"]
const leftPadding = ['pl-10', 'pl-20', 'pl-28', 'pl-48']

Expand Down Expand Up @@ -121,19 +122,18 @@ const MenuItem = ({id, url, title, activeTrail, children, level}: MenuItemProps)
'list-unstyled w-full min-w-[300px] lg:bg-white lg:shadow-2xl px-0 lg:absolute',
zIndexes[level],
{
'block': submenuOpen,
'hidden': !submenuOpen,
'lg:top-full lg:right-0': level === 0,
'lg:top-0': level !== 0,
'lg:left-full': level !== 0 && positionRight,
'lg:right-full': level !== 0 && !positionRight,
'block': submenuOpen,
'hidden': !submenuOpen,
}
)

return (
<li
{...itemProps}
className={"m-0 py-2 lg:py-0 relative border-b first:border-t last:border-0 border-cool-grey lg:border-black-20 lg:relative lg:mr-5 last:lg:mr-0 " + (level === 0 ? "lg:border-b-0 first:border-t-0" : "")}
className={clsx("m-0 py-2 lg:py-0 relative border-b first:border-t last:border-0 border-cool-grey lg:border-black-20 lg:relative lg:mr-5 last:lg:mr-0", level === 0 && "lg:border-b-0 first:border-t-0")}
>
<div className="flex items-center justify-between lg:justify-end">
<Link
Expand All @@ -155,7 +155,7 @@ const MenuItem = ({id, url, title, activeTrail, children, level}: MenuItemProps)
>
<ChevronDownIcon
height={35}
className={(submenuOpen ? "rotate-180" : "") + " transition group-hocus:scale-125 group-hocus:text-black ease-in-out duration-150"}
className={clsx("transition group-hocus:scale-125 group-hocus:text-black ease-in-out duration-150", submenuOpen && "rotate-180")}
/>
<span className="sr-only">{submenuOpen ? "Close" : "Open"} {title} Submenu</span>
</button>
Expand Down
40 changes: 8 additions & 32 deletions src/lib/hooks/useOutsideClick.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
import {useCallback, useRef} from "react";
import {useEventListener} from "usehooks-ts";
import {RefObject} from "react";
import {useOnClickOutside} from "usehooks-ts";

const useOutsideClick = (onClickOutside: (_e: MouseEvent | FocusEvent | TouchEvent) => void) => {
const useOutsideClick = (ref: RefObject<any>, onClickOutside: () => void) => {
useOnClickOutside(ref, onClickOutside, "mousedown")
useOnClickOutside(ref, onClickOutside, "touchstart")

const clickCaptured = useRef(false)
const focusCaptured = useRef(false)

const documentClick = useCallback((event: MouseEvent | TouchEvent | FocusEvent) => {
if (event.type === 'mousedown' || event.type === 'touchstart') {
if (!clickCaptured.current && onClickOutside) {
onClickOutside(event);
}
clickCaptured.current = false;
}

if (event.type == 'focusin') {
if (!focusCaptured.current && onClickOutside) {
onClickOutside(event);
}
focusCaptured.current = false;
}

}, [onClickOutside])

useEventListener('mousedown', documentClick);
useEventListener('touchstart', documentClick);
useEventListener('focusin', documentClick);

return {
onMouseDown: () => clickCaptured.current = true,
onFocus: () => focusCaptured.current = true,
onTouchStart: () => clickCaptured.current = true
}
// @ts-ignore Focus in event works the same way as mousedown.
// @see https://github.com/juliencrn/usehooks-ts/discussions/522
useOnClickOutside(ref, onClickOutside, "focusin")
}

export default useOutsideClick;

0 comments on commit 41b1226

Please sign in to comment.