summaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx26
-rw-r--r--src/components/SearchForm/SearchForm.tsx4
-rw-r--r--src/components/Toolbar/Toolbar.tsx80
3 files changed, 94 insertions, 16 deletions
diff --git a/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx b/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx
index 22da133..246ad80 100644
--- a/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx
+++ b/src/components/Buttons/ButtonToolbar/ButtonToolbar.tsx
@@ -1,19 +1,22 @@
import { CloseIcon, CogIcon, SearchIcon } from '@components/Icons';
import { t } from '@lingui/macro';
-import { SetStateAction } from 'react';
+import { ForwardedRef, forwardRef, SetStateAction } from 'react';
import styles from '../Buttons.module.scss';
type ButtonType = 'search' | 'settings';
-const ButtonToolbar = ({
- type,
- isActivated,
- setIsActivated,
-}: {
- type: ButtonType;
- isActivated: boolean;
- setIsActivated: (value: SetStateAction<boolean>) => void;
-}) => {
+const ButtonToolbar = (
+ {
+ type,
+ isActivated,
+ setIsActivated,
+ }: {
+ type: ButtonType;
+ isActivated: boolean;
+ setIsActivated: (value: SetStateAction<boolean>) => void;
+ },
+ ref: ForwardedRef<HTMLButtonElement>
+) => {
const ButtonIcon = () => (type === 'search' ? <SearchIcon /> : <CogIcon />);
const btnClasses = isActivated
? `${styles.toolbar} ${styles['toolbar--activated']}`
@@ -21,6 +24,7 @@ const ButtonToolbar = ({
return (
<button
+ ref={ref}
className={btnClasses}
type="button"
onClick={() => setIsActivated(!isActivated)}
@@ -42,4 +46,4 @@ const ButtonToolbar = ({
);
};
-export default ButtonToolbar;
+export default forwardRef(ButtonToolbar);
diff --git a/src/components/SearchForm/SearchForm.tsx b/src/components/SearchForm/SearchForm.tsx
index c1a7ca7..cefda85 100644
--- a/src/components/SearchForm/SearchForm.tsx
+++ b/src/components/SearchForm/SearchForm.tsx
@@ -13,10 +13,10 @@ const SearchForm = ({ isOpened }: { isOpened: boolean }) => {
useEffect(() => {
setTimeout(() => {
- if (inputRef.current) {
+ if (isOpened && inputRef.current) {
inputRef.current.focus();
}
- }, 800);
+ }, 400);
}, [isOpened]);
const launchSearch = (e: FormEvent) => {
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<boolean>(false);
const [isSearchOpened, setIsSearchOpened] = useState<boolean>(false);
const [isSettingsOpened, setIsSettingsOpened] = useState<boolean>(false);
+ const searchBtnRef = useRef<HTMLButtonElement>(null);
+ const searchModalRef = useRef<HTMLDivElement>(null);
+ const settingsBtnRef = useRef<HTMLButtonElement>(null);
+ const settingsModalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isNavOpened) {
@@ -31,6 +35,70 @@ const Toolbar = () => {
}
}, [isSettingsOpened]);
+ const isClickOutside = (
+ ref: RefObject<HTMLDivElement>,
+ target: EventTarget
+ ) => {
+ const currentRef = ref.current;
+ return currentRef && !currentRef.contains(target as Node);
+ };
+
+ const isToggleBtn = (ref: RefObject<HTMLDivElement>, 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<HTMLDivElement> | 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 = () => {
<div className={styles.wrapper}>
<MainNav isOpened={isNavOpened} setIsOpened={setIsNavOpened} />
<ButtonToolbar
+ ref={searchBtnRef}
type="search"
isActivated={isSearchOpened}
setIsActivated={setIsSearchOpened}
/>
- <div className={searchClasses}>
+ <div id="search-modal" className={searchClasses} ref={searchModalRef}>
<SearchForm isOpened={isSearchOpened} />
</div>
<ButtonToolbar
+ ref={settingsBtnRef}
type="settings"
isActivated={isSettingsOpened}
setIsActivated={setIsSettingsOpened}
/>
- <div className={settingsClasses}>
+ <div
+ id="settings-modal"
+ className={settingsClasses}
+ ref={settingsModalRef}
+ >
<Settings />
</div>
</div>