diff --git a/apps/docs/components/Clippy/Clippy.tsx b/apps/docs/components/Clippy/Clippy.tsx index 03eba491f30..633ebe5ac26 100644 --- a/apps/docs/components/Clippy/Clippy.tsx +++ b/apps/docs/components/Clippy/Clippy.tsx @@ -1,67 +1,30 @@ import { useTheme } from 'common/Providers' import Image from 'next/image' -import { FC, useCallback, useEffect, useState } from 'react' +import { FC } from 'react' import { createPortal } from 'react-dom' import clippyImageDark from '../../public/img/clippy-dark.png' import clippyImage from '../../public/img/clippy.png' import ClippyBubble from './ClippyBubble' import ClippyModal from './ClippyModal' +import { useClippy } from './ClippyProvider' const Clippy: FC = () => { const { isDarkMode } = useTheme() - 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, - }) + const { isOpen, open, close } = useClippy() return ( <> - {!isModalOpen && ( -
- -
+ {!isOpen && ( +
+ +
Clippy
)} - {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/') && } */}
-
- +
+
+ Clippy +
+