Feat: Render operator menu by category. #3221 (#5302)

### What problem does this PR solve?
Feat: Render operator menu by category. #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2025-02-24 16:51:44 +08:00 committed by GitHub
parent f9f75aa119
commit ca865df87f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 3088 additions and 64 deletions

View File

@ -1,3 +1,4 @@
import { Card, CardContent } from '@/components/ui/card';
import { import {
Collapsible, Collapsible,
CollapsibleContent, CollapsibleContent,
@ -11,81 +12,97 @@ import {
SidebarGroupLabel, SidebarGroupLabel,
SidebarHeader, SidebarHeader,
SidebarMenu, SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from '@/components/ui/sidebar'; } from '@/components/ui/sidebar';
import { ChevronDown } from 'lucide-react';
import { useMemo } from 'react';
import { import {
Calendar, AgentOperatorList,
ChevronDown, Operator,
Home, componentMenuList,
Inbox, operatorMap,
Search, } from './constant';
Settings, import OperatorIcon from './operator-icon';
} from 'lucide-react';
// Menu items. function SideDown() {
const items = [ return (
{ <ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
title: 'Home', );
url: '#', }
icon: Home,
}, type OperatorItem = {
{ name: Operator;
title: 'Inbox', };
url: '#',
icon: Inbox, function OperatorCard({ name }: OperatorItem) {
}, return (
{ <Card className="bg-colors-background-inverse-weak border-colors-outline-neutral-standard">
title: 'Calendar', <CardContent className="p-2 flex items-center gap-2">
url: '#', <OperatorIcon
icon: Calendar, name={name}
}, color={operatorMap[name].color}
{ ></OperatorIcon>
title: 'Search', {name}
url: '#', </CardContent>
icon: Search, </Card>
}, );
{ }
title: 'Settings',
url: '#', type OperatorCollapsibleProps = { operatorList: OperatorItem[]; title: string };
icon: Settings,
}, function OperatorCollapsible({
]; operatorList,
title,
export function AgentSidebar() { }: OperatorCollapsibleProps) {
return ( return (
<Sidebar variant={'floating'} className="top-16">
<SidebarHeader>
<p className="font-bold text-2xl">All nodes</p>
</SidebarHeader>
<SidebarContent>
<Collapsible defaultOpen className="group/collapsible"> <Collapsible defaultOpen className="group/collapsible">
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel asChild> <SidebarGroupLabel asChild className="mb-1">
<CollapsibleTrigger> <CollapsibleTrigger>
Help <span className="font-bold text-base">{title}</span>
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" /> <SideDown />
</CollapsibleTrigger> </CollapsibleTrigger>
</SidebarGroupLabel> </SidebarGroupLabel>
<CollapsibleContent> <CollapsibleContent className="px-2">
<SidebarGroupContent> <SidebarGroupContent>
<SidebarMenu> <SidebarMenu className="gap-2">
{items.map((item) => ( {operatorList.map((item) => (
<SidebarMenuItem key={item.title}> <OperatorCard key={item.name} name={item.name}></OperatorCard>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))} ))}
</SidebarMenu> </SidebarMenu>
</SidebarGroupContent> </SidebarGroupContent>
</CollapsibleContent> </CollapsibleContent>
</SidebarGroup> </SidebarGroup>
</Collapsible> </Collapsible>
<SidebarGroup>yyy</SidebarGroup> );
}
export function AgentSidebar() {
const agentOperatorList = useMemo(() => {
return componentMenuList.filter((x) =>
AgentOperatorList.some((y) => y === x.name),
);
}, []);
const thirdOperatorList = useMemo(() => {
return componentMenuList.filter(
(x) => !AgentOperatorList.some((y) => y === x.name),
);
}, []);
return (
<Sidebar variant={'floating'} className="top-16">
<SidebarHeader>
<p className="font-bold text-2xl">All nodes</p>
</SidebarHeader>
<SidebarContent>
<OperatorCollapsible
title="Agent operator"
operatorList={agentOperatorList}
></OperatorCollapsible>
<OperatorCollapsible
title="Third-party tools"
operatorList={thirdOperatorList}
></OperatorCollapsible>
</SidebarContent> </SidebarContent>
</Sidebar> </Sidebar>
); );

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
import React from 'react';
import { Operator, operatorIconMap } from '../constant';
interface IProps {
name: Operator;
fontSize?: number;
width?: number;
color?: string;
}
const OperatorIcon = ({ name, fontSize, width, color }: IProps) => {
const Icon = operatorIconMap[name] || React.Fragment;
return (
<Icon
className={'text-2xl max-h-6 max-w-6 text-[rgb(59, 118, 244)]'}
style={{ fontSize, color }}
width={width}
></Icon>
);
};
export default OperatorIcon;