All checks were successful
Deploy App / docker (ubuntu-latest) (push) Successful in 3m26s
114 lines
3.0 KiB
TypeScript
114 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import React, { ReactElement, useState } from "react";
|
|
import { DocsContentMetadata } from "@/app/common/documentation";
|
|
import {
|
|
CommandDialog,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandItem,
|
|
CommandList,
|
|
} from "@/app/components/ui/command";
|
|
import { Button, ButtonProps } from "@/app/components/ui/button";
|
|
import { useRouter } from "next/navigation";
|
|
import { cn } from "@/app/common/utils";
|
|
|
|
export function CommandMenu({ ...props }: ButtonProps): ReactElement {
|
|
const router = useRouter();
|
|
|
|
/**
|
|
* Whether to show the search
|
|
*/
|
|
const [open, setOpen] = useState<boolean>(false);
|
|
|
|
/**
|
|
* The pages that were found
|
|
*/
|
|
const [pages, setPages] = useState<DocsContentMetadata[] | undefined>(undefined);
|
|
|
|
// Handle keyboard shortcuts
|
|
React.useEffect(() => {
|
|
const down = (e: KeyboardEvent) => {
|
|
if ((e.key === "k" && (e.metaKey || e.ctrlKey)) || e.key === "/") {
|
|
if (
|
|
(e.target instanceof HTMLElement && e.target.isContentEditable) ||
|
|
e.target instanceof HTMLInputElement ||
|
|
e.target instanceof HTMLTextAreaElement ||
|
|
e.target instanceof HTMLSelectElement
|
|
) {
|
|
return;
|
|
}
|
|
|
|
e.preventDefault();
|
|
setOpen(open => !open);
|
|
}
|
|
};
|
|
|
|
return () => document.removeEventListener("keydown", down);
|
|
}, []);
|
|
|
|
/**
|
|
* Search the documentation
|
|
* for the given query.
|
|
*
|
|
* @param query the query to search for
|
|
*/
|
|
async function searchDocs(query: string): Promise<void> {
|
|
// Don't bother searching if the query is less than 3 characters
|
|
if (query.length < 3) {
|
|
setPages(undefined);
|
|
return;
|
|
}
|
|
|
|
// Attempt to search for the query
|
|
const response = await fetch(`/api/docs/search?query=${query}`);
|
|
const pages: DocsContentMetadata[] = await response.json();
|
|
setPages(pages);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Button
|
|
variant="outline"
|
|
className={cn(
|
|
"relative h-8 w-full justify-start rounded-[0.5rem] bg-background text-sm font-normal text-muted-foreground shadow-none sm:pr-12 md:w-40 lg:w-64",
|
|
props.className,
|
|
)}
|
|
onClick={() => setOpen(true)}
|
|
>
|
|
Search
|
|
</Button>
|
|
|
|
<CommandDialog open={open} onOpenChange={setOpen}>
|
|
<CommandInput
|
|
placeholder="Query..."
|
|
onValueChange={async search => {
|
|
await searchDocs(search);
|
|
}}
|
|
/>
|
|
<CommandList>
|
|
<CommandEmpty>No results found.</CommandEmpty>
|
|
{pages && (
|
|
<CommandGroup heading="Suggestions">
|
|
{pages.map(page => {
|
|
return (
|
|
<CommandItem
|
|
key={page.slug}
|
|
onSelect={() => {
|
|
router.push(`/docs/${page.slug}`);
|
|
setOpen(false);
|
|
}}
|
|
>
|
|
{page.title}
|
|
</CommandItem>
|
|
);
|
|
})}
|
|
</CommandGroup>
|
|
)}
|
|
</CommandList>
|
|
</CommandDialog>
|
|
</>
|
|
);
|
|
}
|