refactor: enhance ToolsDropdownList with Popover and Command components for improved usability

This commit is contained in:
ecwu
2025-01-06 00:29:21 +08:00
parent 22e3760b7f
commit a7bbe1e000

View File

@@ -16,6 +16,22 @@ import {
NavigationMenu, NavigationMenu,
NavigationMenuList, NavigationMenuList,
} from "@/components/ui/navigation-menu"; } from "@/components/ui/navigation-menu";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { BrushIcon } from "lucide-react";
import { useToast } from "@/hooks/use-toast";
interface APITemplateDropdownProps { interface APITemplateDropdownProps {
label: string; label: string;
@@ -133,85 +149,71 @@ function APIsDropdownList({
function ToolsDropdownList() { function ToolsDropdownList() {
const ctx = useContext(AppContext); const ctx = useContext(AppContext);
if (!ctx) return <div>error</div>; if (!ctx) return <div>error</div>;
const { toast } = useToast();
const [open, setOpen] = React.useState(false);
const { chatStore, setChatStore } = ctx; const { chatStore, setChatStore } = ctx;
return ( return (
<NavigationMenuItem> <div className="flex items-center space-x-4 mx-3">
<NavigationMenuTrigger> <p className="text-sm text-muted-foreground">{Tr(`Tools`)}</p>
<span>{Tr(`Tools`)}</span> <Popover open={open} onOpenChange={setOpen}>
</NavigationMenuTrigger> <PopoverTrigger asChild>
<NavigationMenuContent> <Button variant="outline" className="w-[150px] justify-start">
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px]"> {chatStore.toolsString ? (
{ctx.templateTools.map((t, index) => ( <>
<li key={index}> {
<NavigationMenuLink asChild> ctx.templateTools.find(
<a (t) => t.toolsString === chatStore.toolsString
onClick={() => { )?.name
chatStore.toolsString = t.toolsString; }
setChatStore({ ...chatStore }); </>
}} ) : (
className={cn( <>+ {Tr(`Set tools`)}</>
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", )}
chatStore.toolsString === t.toolsString </Button>
? "bg-accent text-accent-foreground" </PopoverTrigger>
: "" <PopoverContent className="p-0" side="right" align="start">
)} <Command>
> <CommandInput placeholder="You can search..." />
<div className="text-sm font-medium leading-none"> <CommandList>
<CommandEmpty>{Tr(`No results found.`)}</CommandEmpty>
<CommandGroup>
{chatStore.toolsString && (
<CommandItem
key={-1}
value=""
onSelect={() => {
chatStore.toolsString = "";
setChatStore({ ...chatStore });
toast({
title: "Tools Cleaned",
description: "Tools cleaned successfully",
});
setOpen(false);
}}
>
<BrushIcon /> {Tr(`Clear tools`)}
</CommandItem>
)}
{ctx.templateTools.map((t, index) => (
<CommandItem
key={index}
value={t.toolsString}
onSelect={(value) => {
chatStore.toolsString = value;
setChatStore({ ...chatStore });
}}
>
{t.name} {t.name}
</div> </CommandItem>
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground"> ))}
{t.toolsString} </CommandGroup>
</p> </CommandList>
</a> </Command>
</NavigationMenuLink> </PopoverContent>
<div className="mt-2 flex justify-between"> </Popover>
<Button </div>
variant="ghost"
size="sm"
onClick={() => {
chatStore.toolsString = "";
setChatStore({ ...chatStore });
}}
>
{Tr(`Clear`)}
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
const name = prompt(`Give **tools** template a name`);
if (!name) return;
t.name = name;
ctx.setTemplateTools(structuredClone(ctx.templateTools));
}}
>
Edit
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => {
if (
!confirm(
`Are you sure to delete this **tools** template?`
)
) {
return;
}
ctx.templateTools.splice(index, 1);
ctx.setTemplateTools(structuredClone(ctx.templateTools));
}}
>
Delete
</Button>
</div>
</li>
))}
</ul>
</NavigationMenuContent>
</NavigationMenuItem>
); );
} }
@@ -219,7 +221,8 @@ const APIListMenu: React.FC = () => {
const ctx = useContext(AppContext); const ctx = useContext(AppContext);
if (!ctx) return <div>error</div>; if (!ctx) return <div>error</div>;
return ( return (
<div className="flex flex-col p-2 gap-2 w-full"> <div className="flex flex-col m-2 gap-2 w-full">
{ctx.templateTools.length > 0 && <ToolsDropdownList />}
<NavigationMenu> <NavigationMenu>
<NavigationMenuList> <NavigationMenuList>
{ctx.templateAPIs.length > 0 && ( {ctx.templateAPIs.length > 0 && (
@@ -260,13 +263,6 @@ const APIListMenu: React.FC = () => {
)} )}
</NavigationMenuList> </NavigationMenuList>
</NavigationMenu> </NavigationMenu>
{ctx.templateTools.length > 0 && (
<NavigationMenu>
<NavigationMenuList>
<ToolsDropdownList />
</NavigationMenuList>
</NavigationMenu>
)}
</div> </div>
); );
}; };