59 lines
1.7 KiB
TypeScript
59 lines
1.7 KiB
TypeScript
import { createContext, lazy, ReactNode, Suspense, useContext, useEffect, useMemo, useState } from "react";
|
|
import type { ScopedCommandAction } from "~/services/command-registry/commandRegistry";
|
|
|
|
type CommandPaletteContextValue = {
|
|
open: boolean;
|
|
setOpen: (open: boolean) => void;
|
|
toggle: () => void;
|
|
};
|
|
|
|
const CommandPaletteContext = createContext<CommandPaletteContextValue | null>(null);
|
|
const CommandPaletteDialog = lazy(() => import("./CommandPaletteDialog"));
|
|
|
|
export function useCommandPalette() {
|
|
const context = useContext(CommandPaletteContext);
|
|
return context ?? { open: false, setOpen: () => undefined, toggle: () => undefined };
|
|
}
|
|
|
|
interface Props {
|
|
layout?: "app" | "admin" | "docs";
|
|
children: ReactNode;
|
|
actions?: ScopedCommandAction[];
|
|
}
|
|
|
|
export default function CommandPalette({ layout, children, actions }: Props) {
|
|
const [open, setOpen] = useState(false);
|
|
|
|
const contextValue = useMemo(
|
|
() => ({
|
|
open,
|
|
setOpen,
|
|
toggle: () => setOpen((value) => !value),
|
|
}),
|
|
[open]
|
|
);
|
|
|
|
useEffect(() => {
|
|
function onKeyDown(event: KeyboardEvent) {
|
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
event.preventDefault();
|
|
setOpen((value) => !value);
|
|
}
|
|
}
|
|
|
|
document.addEventListener("keydown", onKeyDown);
|
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
}, []);
|
|
|
|
return (
|
|
<CommandPaletteContext.Provider value={contextValue}>
|
|
{open ? (
|
|
<Suspense fallback={null}>
|
|
<CommandPaletteDialog actions={actions} layout={layout} open={open} setOpen={setOpen} />
|
|
</Suspense>
|
|
) : null}
|
|
{children}
|
|
</CommandPaletteContext.Provider>
|
|
);
|
|
}
|