diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 29b1b60..9a21622 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,15 +8,12 @@ import { ActiveState } from './models/misc'; export default function App() { //TODO : below media query is disjoint from tailwind. Please wire it together. const [navState, setNavState] = useState( - window.matchMedia('((min-width: 768px)').matches ? 'ACTIVE' : 'INACTIVE', + window.matchMedia('(min-width: 768px)').matches ? 'ACTIVE' : 'INACTIVE', ); return (
- setNavState(val)} - /> +
void; + setNavState: React.Dispatch>; }) { const dispatch = useDispatch(); const docs = useSelector(selectSourceDocs); @@ -41,9 +42,26 @@ export default function Navigation({ const [selectedDocsModalState, setSelectedDocsModalState] = useState(isSelectedDocsSet ? 'INACTIVE' : 'ACTIVE'); + const navRef = useRef(null); + useOutsideAlerter( + navRef, + () => { + if ( + window.matchMedia('(max-width: 768px)').matches && + navState === 'ACTIVE' && + apiKeyModalState === 'INACTIVE' + ) { + setNavState('INACTIVE'); + setIsDocsListOpen(false); + } + }, + [navState, isDocsListOpen, apiKeyModalState], + ); + return ( <>
) : ( - { - if (inputRef.current?.textContent) { - handleQuestion(inputRef.current.textContent); - inputRef.current.textContent = ''; - } - }} - src={Send} - className="relative right-[35px] bottom-[15px] -mr-[21px] cursor-pointer self-end" - > +
+ { + if (inputRef.current?.textContent) { + handleQuestion(inputRef.current.textContent); + inputRef.current.textContent = ''; + } + }} + src={Send} + > +
)}

diff --git a/frontend/src/hooks.ts b/frontend/src/hooks.ts new file mode 100644 index 0000000..eef2c72 --- /dev/null +++ b/frontend/src/hooks.ts @@ -0,0 +1,19 @@ +import { useEffect, RefObject } from 'react'; + +export function useOutsideAlerter( + ref: RefObject, + handler: () => void, + additionalDeps: unknown[], +) { + useEffect(() => { + function handleClickOutside(this: Document, event: MouseEvent) { + if (ref.current && !ref.current.contains(event.target as Node)) { + handler(); + } + } + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref, ...additionalDeps]); +} diff --git a/frontend/src/preferences/APIKeyModal.tsx b/frontend/src/preferences/APIKeyModal.tsx index 5300bf7..d068aa4 100644 --- a/frontend/src/preferences/APIKeyModal.tsx +++ b/frontend/src/preferences/APIKeyModal.tsx @@ -1,7 +1,8 @@ -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { ActiveState } from '../models/misc'; import { selectApiKey, setApiKey } from './preferenceSlice'; +import { useOutsideAlerter } from './../hooks'; export default function APIKeyModal({ modalState, @@ -16,6 +17,20 @@ export default function APIKeyModal({ const apiKey = useSelector(selectApiKey); const [key, setKey] = useState(apiKey); const [isError, setIsError] = useState(false); + const modalRef = useRef(null); + + useOutsideAlerter( + modalRef, + () => { + if ( + window.matchMedia('(max-width: 768px)').matches && + modalState === 'ACTIVE' + ) { + setModalState('INACTIVE'); + } + }, + [modalState], + ); function handleSubmit() { if (key.length <= 1) { @@ -39,7 +54,10 @@ export default function APIKeyModal({ modalState === 'ACTIVE' ? 'visible' : 'hidden' } absolute z-30 h-screen w-screen bg-gray-alpha`} > -

+

OpenAI API Key

Before you can start using DocsGPT we need you to provide an API key