import type { ApiItemKind } from '@microsoft/api-extractor-model'; import { Dialog, useDialogState } from 'ariakit/dialog'; import { Command } from 'cmdk'; import { useRouter } from 'next/router'; import { useEffect, useMemo, useState } from 'react'; import { VscArrowRight, VscPackage, VscSymbolClass, VscSymbolEnum, VscSymbolField, VscSymbolInterface, VscSymbolMethod, VscSymbolProperty, VscSymbolVariable, VscVersions, } from 'react-icons/vsc'; import { useKey } from 'react-use'; import useSWR from 'swr'; import { PACKAGES } from '~/util/constants'; import { fetcher } from '~/util/fetcher'; import { client } from '~/util/search'; function resolveIcon(item: keyof ApiItemKind) { switch (item) { case 'Class': return ; case 'Enum': return ; case 'Interface': return ; case 'Property': return ; case 'TypeAlias': return ; case 'Variables': return ; default: return ; } } export function CmdkDialog({ currentPackageName }: { currentPackageName?: string | undefined }) { const router = useRouter(); const dialog = useDialogState(); const [search, setSearch] = useState(''); const [page, setPage] = useState(''); const [packageName, setPackageName] = useState(''); const [searchResults, setSearchResults] = useState([]); const { data: versions, isValidating } = useSWR( packageName ? `https://docs.discordjs.dev/api/info?package=${packageName}` : null, fetcher, ); const packageCommandItems = useMemo( () => PACKAGES.map((pkg) => ( { setPackageName(pkg); setPage('version'); setSearch(''); }} > {pkg} )), [], ); const versionCommandItems = useMemo( () => versions ?.map((version) => ( { void router.push(`/docs/packages/${packageName}/${version}`); dialog.setOpen(false); }} > {version} )) .reverse() ?? [], // eslint-disable-next-line react-hooks/exhaustive-deps [packageName], ); const searchResultItems = useMemo( () => searchResults?.map((item) => ( { void router.push(item.path); dialog.setOpen(false); }} > {resolveIcon(item.kind)} {item.name} {item.summary} {item.path} )) ?? [], // eslint-disable-next-line react-hooks/exhaustive-deps [searchResults], ); useKey( (event) => { if (event.key === 'k' && (event.metaKey || event.ctrlKey)) { event.preventDefault(); return true; } return false; }, dialog.toggle, { event: 'keydown', options: {} }, [], ); useKey( (event) => event.key === 'Backspace' && !search, () => setPage(''), { event: 'keydown' }, [], ); useEffect(() => { if (!dialog.open) { setSearch(''); setPage(''); } }, [dialog.open]); useEffect(() => { const searchDoc = async (searchString: string) => { const res = await client.index(`${currentPackageName}-main`).search(searchString, { limit: 5 }); setSearchResults(res.hits); }; if (search && currentPackageName) { void searchDoc(search); } else { setSearchResults([]); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [search]); return ( No results found {isValidating ? ( Loading... ) : null} {page || search ? null : packageCommandItems} {page === 'version' && !search ? versionCommandItems : null} {search ? searchResultItems : null} ); }