Files
multica/packages/views/projects/components/project-picker.tsx
Naiyuan Qing e268ee3e71 refactor(views): centralize project icon rendering and fix nav active state (#1738)
Extract <ProjectIcon> with sm/md/lg sizes and a single 📁 fallback,
replacing 9 inline render sites that had drifted into 6 different
sizes and a mixed FolderKanban/emoji fallback.

Two visible fixes fall out of the centralization:
- ProjectPicker trigger now shows the selected project's icon (most
  visibly in the issue detail right Properties panel, where it had
  always been a generic FolderKanban).
- Sidebar parent nav (Projects, Issues, Settings, ...) now stays
  highlighted on child detail routes via a small isNavActive helper.
  Pinned items keep strict equality.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:42:56 +08:00

67 lines
2.4 KiB
TypeScript

"use client";
import { Check, FolderKanban, X } from "lucide-react";
import { useQuery } from "@tanstack/react-query";
import { projectListOptions } from "@multica/core/projects/queries";
import { useWorkspaceId } from "@multica/core/hooks";
import type { UpdateIssueRequest } from "@multica/core/types";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuSeparator,
} from "@multica/ui/components/ui/dropdown-menu";
import { ProjectIcon } from "./project-icon";
export function ProjectPicker({
projectId,
onUpdate,
triggerRender,
align = "start",
}: {
projectId: string | null;
onUpdate: (updates: Partial<UpdateIssueRequest>) => void;
triggerRender?: React.ReactElement;
align?: "start" | "center" | "end";
}) {
const wsId = useWorkspaceId();
const { data: projects = [] } = useQuery(projectListOptions(wsId));
const current = projects.find((p) => p.id === projectId);
return (
<DropdownMenu>
<DropdownMenuTrigger
className={triggerRender ? undefined : "flex items-center gap-1.5 cursor-pointer rounded px-1 -mx-1 hover:bg-accent/30 transition-colors overflow-hidden"}
render={triggerRender}
>
{current ? (
<ProjectIcon project={current} size="sm" />
) : (
<FolderKanban className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
)}
<span className="truncate">{current ? current.title : "No project"}</span>
</DropdownMenuTrigger>
<DropdownMenuContent align={align} className="w-52">
{projects.map((p) => (
<DropdownMenuItem key={p.id} onClick={() => onUpdate({ project_id: p.id })}>
<ProjectIcon project={p} size="md" className="mr-1" />
<span className="truncate">{p.title}</span>
{p.id === projectId && <Check className="ml-auto h-3.5 w-3.5 shrink-0" />}
</DropdownMenuItem>
))}
{projects.length > 0 && projectId && <DropdownMenuSeparator />}
{projectId && (
<DropdownMenuItem onClick={() => onUpdate({ project_id: null })}>
<X className="h-3.5 w-3.5 text-muted-foreground" />
Remove from project
</DropdownMenuItem>
)}
{projects.length === 0 && (
<div className="px-2 py-1.5 text-xs text-muted-foreground">No projects yet</div>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}