feat(docs-gpt): improved mobile responsiveness

This commit is contained in:
Greg Richardson committed 2023-02-05 14:34:07 -07:00
1 parent a0b016ae84
commit 1bc133596c
6 files changed
+103 -52

No files matched your search

+8 -45
View File
@@ -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 && (
<div className="flex flex-col items-end gap-1 md:gap-4">
<ClippyBubble onClick={onOpen} />
<div className="w-[120px] md:w-[150px] p-8 md:p-0">
{!isOpen && (
<div className="hidden md:flex flex-col items-end gap-1 md:gap-4">
<ClippyBubble onClick={open} />
<div className="w-[100px] lg:w-[150px] p-8 md:p-0">
<Image src={isDarkMode ? clippyImageDark : clippyImage} alt="Clippy" />
</div>
</div>
)}
{isModalOpen && createPortal(<ClippyModal onClose={onClose} />, document.body)}
{isOpen && createPortal(<ClippyModal onClose={close} />, 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])
}
+1 -1
View File
@@ -9,7 +9,7 @@ interface Props {
const ClippyBubble: FC<Props> = ({ className, onClick }) => {
return (
<div
className={`flex gap-2 border bg-white dark:bg-scale-300 relative rounded-xl p-6 max-w-sm md:max-w-xs shadow-xl mb-1 md:mb-4 cursor-pointer hover:bg-scale-200 transition-colors border-scale-500 hover:border-scale-700 ${className}`}
className={`flex gap-2 border bg-white dark:bg-scale-300 relative rounded-xl p-4 lg:p-6 max-w-sm lg:max-w-xs shadow-xl mb-1 lg:mb-4 cursor-pointer hover:bg-scale-200 transition-colors border-scale-500 hover:border-scale-700 ${className}`}
onClick={onClick}
>
<img
+1 -1
View File
@@ -104,7 +104,7 @@ const ClippyModal: FC<Props> = ({ onClose }) => {
return (
<Modal size="xlarge" visible={true} onCancel={onClose} closable={false} hideFooter>
<div
className={`mx-auto max-h-[75vh] flex flex-col gap-4 rounded-lg p-4 md:pt-6 md:px-6 pb-2 w-full max-w-3xl shadow-2xl overflow-hidden border text-left border-scale-500 bg-scale-100 dark:bg-scale-300 cursor-auto relative min-w-[340px]`}
className={`mx-auto max-h-[75vh] flex flex-col gap-4 rounded-lg p-4 md:pt-6 md:px-6 pb-2 w-full shadow-2xl overflow-hidden border text-left border-scale-500 bg-scale-100 dark:bg-scale-300 cursor-auto relative min-w-[340px]`}
onClick={(e) => e.stopPropagation()}
>
<div className="relative">
@@ -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<ClippyContextValue>(null)
export const useClippy = () => {
const { isOpen, open, close } = useContext(ClippyContext)
return { isOpen, open, close }
}
const ClippyProvider: FC<PropsWithChildren<{}>> = ({ 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 (
<ClippyContext.Provider value={{ isOpen: isModalOpen, open: onOpen, close: onClose }}>
{children}
</ClippyContext.Provider>
)
}
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
@@ -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/') && <RefSwitcher />} */}
</div>
<div className="flex items-center gap-12">
<SearchButton className="w-full lg:w-96">
<div className="flex items-center gap-6">
<div className="flex md:hidden cursor-pointer">
<Image
onClick={openClippy}
width={26}
height={29}
src={isDarkMode ? clippyImageDark : clippyImage}
alt="Clippy"
/>
</div>
<SearchButton className="md:w-full lg:w-96">
<div
className="
flex
+6 -3
View File
@@ -5,6 +5,7 @@ import { DefaultSeo } from 'next-seo'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { AppPropsWithLayout } from 'types'
import ClippyProvider from '~/components/Clippy/ClippyProvider'
import { SearchProvider } from '~/components/DocSearch'
import Favicons from '~/components/Favicons'
import SiteLayout from '~/layouts/SiteLayout'
@@ -87,9 +88,11 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
<SessionContextProvider supabaseClient={supabase}>
<ThemeProvider>
<SearchProvider>
<SiteLayout>
<Component {...pageProps} />
</SiteLayout>
<ClippyProvider>
<SiteLayout>
<Component {...pageProps} />
</SiteLayout>
</ClippyProvider>
</SearchProvider>
</ThemeProvider>
</SessionContextProvider>