mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-15 02:53:31 +01:00
feat: searchbar
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import type { ApiItemKind } from '@microsoft/api-extractor-model';
|
import type { ApiItemKind } from '@microsoft/api-extractor-model';
|
||||||
import { Dialog, useDialogState } from 'ariakit/dialog';
|
import { Dialog } from 'ariakit/dialog';
|
||||||
import { Command } from 'cmdk';
|
import { Command } from 'cmdk';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from 'react-icons/vsc';
|
} from 'react-icons/vsc';
|
||||||
import { useKey } from 'react-use';
|
import { useKey } from 'react-use';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
|
import { useCmdK } from '~/contexts/cmdK';
|
||||||
import { PACKAGES } from '~/util/constants';
|
import { PACKAGES } from '~/util/constants';
|
||||||
import { fetcher } from '~/util/fetcher';
|
import { fetcher } from '~/util/fetcher';
|
||||||
import { client } from '~/util/search';
|
import { client } from '~/util/search';
|
||||||
@@ -40,9 +41,9 @@ function resolveIcon(item: keyof ApiItemKind) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CmdkDialog({ currentPackageName }: { currentPackageName?: string | undefined }) {
|
export function CmdKDialog({ currentPackageName }: { currentPackageName?: string | undefined }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const dialog = useDialogState();
|
const dialog = useCmdK();
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
const [page, setPage] = useState('');
|
const [page, setPage] = useState('');
|
||||||
const [packageName, setPackageName] = useState('');
|
const [packageName, setPackageName] = useState('');
|
||||||
@@ -86,7 +87,7 @@ export function CmdkDialog({ currentPackageName }: { currentPackageName?: string
|
|||||||
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-offset-0 [&[aria-selected]]:ring-width-4 [&[aria-selected]]:ring flex flex transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white"
|
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-offset-0 [&[aria-selected]]:ring-width-4 [&[aria-selected]]:ring flex flex transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
void router.push(`/docs/packages/${packageName}/${version}`);
|
void router.push(`/docs/packages/${packageName}/${version}`);
|
||||||
dialog.setOpen(false);
|
dialog!.setOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||||
@@ -111,13 +112,13 @@ export function CmdkDialog({ currentPackageName }: { currentPackageName?: string
|
|||||||
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-offset-0 [&[aria-selected]]:ring-width-4 [&[aria-selected]]:ring flex flex transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white"
|
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-offset-0 [&[aria-selected]]:ring-width-4 [&[aria-selected]]:ring flex flex transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
void router.push(item.path);
|
void router.push(item.path);
|
||||||
dialog.setOpen(false);
|
dialog!.setOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||||
<div className="flex flex-row place-items-center gap-4">
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
{resolveIcon(item.kind)}
|
{resolveIcon(item.kind)}
|
||||||
<div className="w-50 flex flex-col sm:w-full">
|
<div className="w-50 sm:w-100 flex flex-col">
|
||||||
<h2 className="font-semibold">{item.name}</h2>
|
<h2 className="font-semibold">{item.name}</h2>
|
||||||
<div className="line-clamp-1 text-sm font-normal">{item.summary}</div>
|
<div className="line-clamp-1 text-sm font-normal">{item.summary}</div>
|
||||||
<div className="line-clamp-1 hidden text-xs font-light opacity-75 dark:opacity-50 sm:block">
|
<div className="line-clamp-1 hidden text-xs font-light opacity-75 dark:opacity-50 sm:block">
|
||||||
@@ -142,7 +143,7 @@ export function CmdkDialog({ currentPackageName }: { currentPackageName?: string
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
dialog.toggle,
|
dialog!.toggle,
|
||||||
{ event: 'keydown', options: {} },
|
{ event: 'keydown', options: {} },
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@@ -154,11 +155,12 @@ export function CmdkDialog({ currentPackageName }: { currentPackageName?: string
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!dialog.open) {
|
if (!dialog!.open) {
|
||||||
setSearch('');
|
setSearch('');
|
||||||
setPage('');
|
setPage('');
|
||||||
}
|
}
|
||||||
}, [dialog.open]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [dialog!.open]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const searchDoc = async (searchString: string) => {
|
const searchDoc = async (searchString: string) => {
|
||||||
@@ -175,7 +177,7 @@ export function CmdkDialog({ currentPackageName }: { currentPackageName?: string
|
|||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog className="fixed top-1/4 left-1/2 z-50 -translate-x-1/2" state={dialog}>
|
<Dialog className="fixed top-1/4 left-1/2 z-50 -translate-x-1/2" state={dialog!}>
|
||||||
<Command
|
<Command
|
||||||
label="Command Menu"
|
label="Command Menu"
|
||||||
className="dark:bg-dark-300 min-w-xs sm:min-w-lg max-w-xs rounded bg-white sm:max-w-lg"
|
className="dark:bg-dark-300 min-w-xs sm:min-w-lg max-w-xs rounded bg-white sm:max-w-lg"
|
||||||
@@ -183,7 +185,7 @@ export function CmdkDialog({ currentPackageName }: { currentPackageName?: string
|
|||||||
>
|
>
|
||||||
<Command.Input
|
<Command.Input
|
||||||
className="dark:bg-dark-300 caret-blurple placeholder:text-dark-300/75 mt-4 border-0 bg-white p-4 pt-0 text-lg outline-0 dark:placeholder:text-white/75"
|
className="dark:bg-dark-300 caret-blurple placeholder:text-dark-300/75 mt-4 border-0 bg-white p-4 pt-0 text-lg outline-0 dark:placeholder:text-white/75"
|
||||||
placeholder="Type to search..."
|
placeholder="Quick search..."
|
||||||
value={search}
|
value={search}
|
||||||
onValueChange={setSearch}
|
onValueChange={setSearch}
|
||||||
/>
|
/>
|
||||||
@@ -8,12 +8,22 @@ import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
|||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
import { type PropsWithChildren, useState, useEffect, useMemo, Fragment } from 'react';
|
import { type PropsWithChildren, useState, useEffect, useMemo, Fragment } from 'react';
|
||||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||||
import { VscChevronDown, VscColorMode, VscGithubInverted, VscMenu, VscPackage, VscVersions } from 'react-icons/vsc';
|
import { FiCommand } from 'react-icons/fi';
|
||||||
|
import {
|
||||||
|
VscChevronDown,
|
||||||
|
VscColorMode,
|
||||||
|
VscGithubInverted,
|
||||||
|
VscMenu,
|
||||||
|
VscPackage,
|
||||||
|
VscSearch,
|
||||||
|
VscVersions,
|
||||||
|
} from 'react-icons/vsc';
|
||||||
import { useMedia /* useLockBodyScroll */ } from 'react-use';
|
import { useMedia /* useLockBodyScroll */ } from 'react-use';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import vercelLogo from '../assets/powered-by-vercel.svg';
|
import vercelLogo from '../assets/powered-by-vercel.svg';
|
||||||
import { CmdkDialog } from './Cmdk';
|
import { CmdKDialog } from './CmdK';
|
||||||
import { SidebarItems } from './SidebarItems';
|
import { SidebarItems } from './SidebarItems';
|
||||||
|
import { useCmdK } from '~/contexts/cmdK';
|
||||||
import { PACKAGES } from '~/util/constants';
|
import { PACKAGES } from '~/util/constants';
|
||||||
import { fetcher } from '~/util/fetcher';
|
import { fetcher } from '~/util/fetcher';
|
||||||
import type { findMember } from '~/util/model.server';
|
import type { findMember } from '~/util/model.server';
|
||||||
@@ -49,6 +59,7 @@ export function SidebarLayout({
|
|||||||
children,
|
children,
|
||||||
}: PropsWithChildren<Partial<SidebarLayoutProps>>) {
|
}: PropsWithChildren<Partial<SidebarLayoutProps>>) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const dialog = useCmdK();
|
||||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||||
const { data: versions } = useSWR<string[]>(`https://docs.discordjs.dev/api/info?package=${packageName}`, fetcher);
|
const { data: versions } = useSWR<string[]>(`https://docs.discordjs.dev/api/info?package=${packageName}`, fetcher);
|
||||||
const { resolvedTheme, setTheme } = useTheme();
|
const { resolvedTheme, setTheme } = useTheme();
|
||||||
@@ -157,7 +168,20 @@ export function SidebarLayout({
|
|||||||
<VscMenu size={24} />
|
<VscMenu size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
<div className="hidden md:flex md:flex-row">{breadcrumbs}</div>
|
<div className="hidden md:flex md:flex-row">{breadcrumbs}</div>
|
||||||
<div className="flex flex-row gap-4">
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<Button
|
||||||
|
as="div"
|
||||||
|
className="dark:bg-dark-800 rounded bg-white px-4 py-2.5"
|
||||||
|
onClick={() => dialog?.toggle()}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row place-items-center gap-4">
|
||||||
|
<VscSearch size={18} />
|
||||||
|
<span className="opacity-65">Search...</span>
|
||||||
|
<div className="opacity-65 flex flex-row place-items-center gap-2">
|
||||||
|
<FiCommand size={18} /> K
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
as="a"
|
as="a"
|
||||||
className="flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline active:translate-y-px"
|
className="flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline active:translate-y-px"
|
||||||
@@ -315,7 +339,7 @@ export function SidebarLayout({
|
|||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
<CmdkDialog currentPackageName={packageName} />
|
<CmdKDialog currentPackageName={packageName} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
15
packages/website/src/contexts/cmdK.tsx
Normal file
15
packages/website/src/contexts/cmdK.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { type DisclosureState, useDialogState } from 'ariakit';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
export const CmdKContext = createContext<DisclosureState | null>(null);
|
||||||
|
|
||||||
|
export const CmdKProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
const dialog = useDialogState();
|
||||||
|
|
||||||
|
return <CmdKContext.Provider value={dialog}>{children}</CmdKContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useCmdK() {
|
||||||
|
return useContext(CmdKContext);
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ import { Function } from '~/components/model/Function';
|
|||||||
import { Interface } from '~/components/model/Interface';
|
import { Interface } from '~/components/model/Interface';
|
||||||
import { TypeAlias } from '~/components/model/TypeAlias';
|
import { TypeAlias } from '~/components/model/TypeAlias';
|
||||||
import { Variable } from '~/components/model/Variable';
|
import { Variable } from '~/components/model/Variable';
|
||||||
|
import { CmdKProvider } from '~/contexts/cmdK';
|
||||||
import { MemberProvider } from '~/contexts/member';
|
import { MemberProvider } from '~/contexts/member';
|
||||||
import { PACKAGES } from '~/util/constants';
|
import { PACKAGES } from '~/util/constants';
|
||||||
import { findMember, findMemberByKey } from '~/util/model.server';
|
import { findMember, findMemberByKey } from '~/util/model.server';
|
||||||
@@ -256,23 +257,25 @@ export default function SlugPage(props: Partial<SidebarLayoutProps & { error?: s
|
|||||||
return props.error ? (
|
return props.error ? (
|
||||||
<div className="flex h-full max-h-full w-full max-w-full flex-row">{props.error}</div>
|
<div className="flex h-full max-h-full w-full max-w-full flex-row">{props.error}</div>
|
||||||
) : (
|
) : (
|
||||||
<MemberProvider member={props.data?.member}>
|
<CmdKProvider>
|
||||||
<SidebarLayout {...props}>
|
<MemberProvider member={props.data?.member}>
|
||||||
{props.data?.member ? (
|
<SidebarLayout {...props}>
|
||||||
<>
|
{props.data?.member ? (
|
||||||
<Head>
|
<>
|
||||||
<title key="title">{name}</title>
|
<Head>
|
||||||
<meta key="og_title" property="og:title" content={ogTitle} />
|
<title key="title">{name}</title>
|
||||||
</Head>
|
<meta key="og_title" property="og:title" content={ogTitle} />
|
||||||
{member(props.data.member)}
|
</Head>
|
||||||
</>
|
{member(props.data.member)}
|
||||||
) : props.data?.source ? (
|
</>
|
||||||
<div className="prose max-w-none">
|
) : props.data?.source ? (
|
||||||
<MDXRemote {...props.data.source} />
|
<div className="prose max-w-none">
|
||||||
</div>
|
<MDXRemote {...props.data.source} />
|
||||||
) : null}
|
</div>
|
||||||
</SidebarLayout>
|
) : null}
|
||||||
</MemberProvider>
|
</SidebarLayout>
|
||||||
|
</MemberProvider>
|
||||||
|
</CmdKProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ export default function IndexRoute() {
|
|||||||
>
|
>
|
||||||
Guide <FiExternalLink />
|
Guide <FiExternalLink />
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 rounded border bg-transparent px-4 text-base font-semibold leading-none text-black no-underline active:translate-y-px dark:text-white"
|
||||||
|
href="https://github.com/discordjs/discord.js"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
GitHub <FiExternalLink />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<SyntaxHighlighter code={CODE_EXAMPLE} />
|
<SyntaxHighlighter code={CODE_EXAMPLE} />
|
||||||
|
|||||||
Reference in New Issue
Block a user