From 88478d6b991de50582a6ef85781eed5f56d68dbb Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 14 Jan 2022 11:42:34 +0100 Subject: chore(toolbar): close modals on click/focus outside --- src/components/Toolbar/Toolbar.tsx | 80 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) (limited to 'src/components/Toolbar/Toolbar.tsx') diff --git a/src/components/Toolbar/Toolbar.tsx b/src/components/Toolbar/Toolbar.tsx index 771efa1..25418b1 100644 --- a/src/components/Toolbar/Toolbar.tsx +++ b/src/components/Toolbar/Toolbar.tsx @@ -2,13 +2,17 @@ import { ButtonToolbar } from '@components/Buttons'; import MainNav from '@components/MainNav/MainNav'; import SearchForm from '@components/SearchForm/SearchForm'; import Settings from '@components/Settings/Settings'; -import { useEffect, useState } from 'react'; +import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; import styles from './Toolbar.module.scss'; const Toolbar = () => { const [isNavOpened, setIsNavOpened] = useState(false); const [isSearchOpened, setIsSearchOpened] = useState(false); const [isSettingsOpened, setIsSettingsOpened] = useState(false); + const searchBtnRef = useRef(null); + const searchModalRef = useRef(null); + const settingsBtnRef = useRef(null); + const settingsModalRef = useRef(null); useEffect(() => { if (isNavOpened) { @@ -31,6 +35,70 @@ const Toolbar = () => { } }, [isSettingsOpened]); + const isClickOutside = ( + ref: RefObject, + target: EventTarget + ) => { + const currentRef = ref.current; + return currentRef && !currentRef.contains(target as Node); + }; + + const isToggleBtn = (ref: RefObject, target: EventTarget) => { + const currentRef = ref.current; + return ( + currentRef && + currentRef.previousElementSibling && + currentRef.previousElementSibling.contains(target as Node) + ); + }; + + const isSearchBtn = useCallback((target: HTMLElement) => { + return ( + target === searchBtnRef.current || searchBtnRef.current?.contains(target) + ); + }, []); + + const isSettingsBtn = useCallback((target: HTMLElement) => { + return ( + target === settingsBtnRef.current || + settingsBtnRef.current?.contains(target) + ); + }, []); + + const handleVisibility = useCallback( + (e: MouseEvent | FocusEvent) => { + let ref: RefObject | null = null; + if (isSearchOpened) ref = searchModalRef; + if (isSettingsOpened) ref = settingsModalRef; + + if (!ref || !ref.current || !ref.current.id) return; + if (!isClickOutside(ref, e.target as Node)) return; + if (isToggleBtn(ref, e.target as Node)) return; + + if ( + ref.current.id === 'search-modal' && + !isSettingsBtn(e.target as HTMLElement) + ) + setIsSearchOpened(false); + if ( + ref.current.id === 'settings-modal' && + !isSearchBtn(e.target as HTMLElement) + ) + setIsSettingsOpened(false); + }, + [isSearchOpened, isSettingsOpened, isSearchBtn, isSettingsBtn] + ); + + useEffect(() => { + document.addEventListener('mousedown', handleVisibility); + document.addEventListener('focusin', handleVisibility); + + return () => { + document.removeEventListener('mousedown', handleVisibility); + document.removeEventListener('focusin', handleVisibility); + }; + }, [handleVisibility]); + const searchClasses = `${styles.menu} ${ isSearchOpened ? styles['menu--opened'] : styles['menu--closed'] }`; @@ -43,19 +111,25 @@ const Toolbar = () => {
-
+
-
+
-- cgit v1.2.3