+ {!isOpen && (
+
)}
- {isModalOpen && createPortal(
, document.body)}
+ {isOpen && createPortal(
, document.body)}
>
)
}
export default Clippy
-
-function useSearchKeyboardEvents({ onOpen, onClose }) {
- useEffect(() => {
- function onKeyDown(event: KeyboardEvent) {
- switch (event.key) {
- case 'Escape':
- onClose()
- return
- case 'j':
- if (event.metaKey || event.ctrlKey) {
- onOpen()
- }
- return
- }
- }
-
- window.addEventListener('keydown', onKeyDown)
-
- return () => {
- window.removeEventListener('keydown', onKeyDown)
- }
- }, [onOpen, onClose])
-}
diff --git a/apps/docs/components/Clippy/ClippyBubble.tsx b/apps/docs/components/Clippy/ClippyBubble.tsx
index 673b3ee286d..0eaf719246a 100644
--- a/apps/docs/components/Clippy/ClippyBubble.tsx
+++ b/apps/docs/components/Clippy/ClippyBubble.tsx
@@ -9,7 +9,7 @@ interface Props {
const ClippyBubble: FC
= ({ className, onClick }) => {
return (
![]()
= ({ onClose }) => {
return (
e.stopPropagation()}
>
diff --git a/apps/docs/components/Clippy/ClippyProvider.tsx b/apps/docs/components/Clippy/ClippyProvider.tsx
new file mode 100644
index 00000000000..47edae2825b
--- /dev/null
+++ b/apps/docs/components/Clippy/ClippyProvider.tsx
@@ -0,0 +1,72 @@
+import {
+ createContext,
+ FC,
+ PropsWithChildren,
+ useCallback,
+ useContext,
+ useEffect,
+ useState,
+} from 'react'
+
+export type ClippyContextValue = {
+ isOpen: boolean
+ open: () => void
+ close: () => void
+}
+
+export const ClippyContext = createContext
(null)
+
+export const useClippy = () => {
+ const { isOpen, open, close } = useContext(ClippyContext)
+
+ return { isOpen, open, close }
+}
+
+const ClippyProvider: FC> = ({ children }) => {
+ const [isModalOpen, setIsModalOpen] = useState(false)
+
+ const onOpen = useCallback(() => {
+ setIsModalOpen(true)
+ document.body.classList.add('DocSearch--active')
+ }, [])
+
+ const onClose = useCallback(() => {
+ setIsModalOpen(false)
+ document.body.classList.remove('DocSearch--active')
+ }, [])
+
+ useSearchKeyboardEvents({
+ onOpen,
+ onClose,
+ })
+ return (
+
+ {children}
+
+ )
+}
+
+function useSearchKeyboardEvents({ onOpen, onClose }) {
+ useEffect(() => {
+ function onKeyDown(event: KeyboardEvent) {
+ switch (event.key) {
+ case 'Escape':
+ onClose()
+ return
+ case 'j':
+ if (event.metaKey || event.ctrlKey) {
+ onOpen()
+ }
+ return
+ }
+ }
+
+ window.addEventListener('keydown', onKeyDown)
+
+ return () => {
+ window.removeEventListener('keydown', onKeyDown)
+ }
+ }, [onOpen, onClose])
+}
+
+export default ClippyProvider
diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavBarRef.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavBarRef.tsx
index b1d344b081d..4588fc5864e 100644
--- a/apps/docs/components/Navigation/NavigationMenu/TopNavBarRef.tsx
+++ b/apps/docs/components/Navigation/NavigationMenu/TopNavBarRef.tsx
@@ -6,13 +6,17 @@ import { Button, IconCommand, IconGitHub, IconMoon, IconSearch, IconSun } from '
import { SearchButton } from '~/components/DocSearch'
import { REFERENCES } from '~/components/Navigation/Navigation.constants'
import { useTheme } from 'common/Providers'
+import clippyImageDark from '../../../public/img/clippy-dark.png'
+import clippyImage from '../../../public/img/clippy.png'
import { getPageType } from '~/lib/helpers'
+import { useClippy } from '~/components/Clippy/ClippyProvider'
const TopNavBarRef: FC = () => {
const { isDarkMode, toggleTheme } = useTheme()
const [mounted, setMounted] = useState(false)
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
+ const { open: openClippy } = useClippy()
const { asPath, push } = useRouter()
const pathSegments = asPath.split('/')
@@ -84,8 +88,17 @@ const TopNavBarRef: FC = () => {
{/* {router.asPath.includes('/reference/') && } */}
-