Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/components/ui/commandPalettes/CommandPaletteDialog.tsx
T

89 lines
3.2 KiB
TypeScript

import { useTranslation } from "react-i18next";
import { useFetcher, useNavigate } from "react-router";
import { UI_SURFACE_IDS } from "~/application/ui/uiRegistry";
import { CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command";
import { getCommandRegistryActions, type CommandRegistryLayout, type ScopedCommandAction } from "~/services/command-registry/commandRegistry";
import type { AppLoaderData } from "~/utils/data/useAppData";
import { useAppOrAdminData } from "~/utils/data/useAppOrAdminData";
import { useRootData } from "~/utils/data/useRootData";
export default function CommandPaletteDialog({
actions,
layout,
open,
setOpen,
}: {
actions?: ScopedCommandAction[];
layout?: CommandRegistryLayout;
open: boolean;
setOpen: (open: boolean) => void;
}) {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const fetcher = useFetcher();
const appData = useAppOrAdminData() as AppLoaderData | undefined;
const rootData = useRootData();
const resolvedActions =
actions ??
(layout
? getCommandRegistryActions({
layout,
t,
i18n,
navigate,
submitLogout: () => {
const form = new FormData();
if (rootData?.csrf) {
form.set("csrf", rootData.csrf);
}
fetcher.submit(form, { method: "post", action: "/logout" });
},
appData,
rootData,
})
: []);
return (
<div>
<CommandDialog
open={open}
onOpenChange={setOpen}
dataUiId={UI_SURFACE_IDS.commandPalettePortal}
onCloseAutoFocus={(event) => {
// The palette opens via controlled state, not a Radix trigger, so Radix
// drops focus to <body> on close. Return focus to the search trigger.
const trigger = typeof document !== "undefined" ? document.querySelector<HTMLElement>('[data-ui-id="ui.action.shell.search"]') : null;
if (trigger) {
event.preventDefault();
trigger.focus();
}
}}
>
<CommandInput data-ui-id={UI_SURFACE_IDS.commandPaletteSearch} />
<CommandList data-ui-id={UI_SURFACE_IDS.commandPaletteResults}>
<CommandEmpty>{t("app.commands.empty.title")}</CommandEmpty>
<CommandGroup>
{resolvedActions.map((action) => (
<CommandItem
key={action.id}
data-ui-id={UI_SURFACE_IDS.commandPaletteRow}
value={[action.name, action.subtitle, action.keywords, action.id].filter(Boolean).join(" ")}
onSelect={() => {
action.perform?.(action);
setOpen(false);
}}
>
<div className="flex w-full items-center justify-between gap-2">
<div className="text-text-neutral-primary/80 text-sm font-medium">{action.name}</div>
{action.subtitle ? <div className="text-text-neutral-tertiary truncate text-xs">{action.subtitle}</div> : null}
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</CommandDialog>
</div>
);
}