Files
multica/packages/views/projects/components/project-chip.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

61 lines
1.8 KiB
TypeScript

"use client";
import { useQuery } from "@tanstack/react-query";
import { projectListOptions, projectDetailOptions } from "@multica/core/projects/queries";
import { useWorkspaceId } from "@multica/core/hooks";
import { ProjectIcon } from "./project-icon";
/**
* Compact presentational representation of a project —
* `<emoji> <title>`, bordered, truncating to max-w-72. Mirror of IssueChip.
*
* Not a link / button: callers wrap it in whatever interactive shell they
* need. Pure UI — data is queried internally so callers can pass just an id.
*/
export interface ProjectChipProps {
projectId: string;
/** Shown when the project can't be resolved. */
fallbackLabel?: string;
/** Extra classes — callers layer interaction hints here. */
className?: string;
}
const BASE_CLASS =
"project-chip inline-flex items-center gap-1.5 rounded-md border mx-0.5 px-2 py-0.5 text-xs max-w-72";
export function ProjectChip({
projectId,
fallbackLabel,
className,
}: ProjectChipProps) {
const wsId = useWorkspaceId();
const { data: projects = [] } = useQuery(projectListOptions(wsId));
const listProject = projects.find((p) => p.id === projectId);
const { data: detailProject } = useQuery({
...projectDetailOptions(wsId, projectId),
enabled: !listProject,
});
const project = listProject ?? detailProject;
const cls = className ? `${BASE_CLASS} ${className}` : BASE_CLASS;
if (!project) {
return (
<span className={cls}>
<ProjectIcon size="md" />
<span className="text-muted-foreground truncate">
{fallbackLabel ?? "Project"}
</span>
</span>
);
}
return (
<span className={cls}>
<ProjectIcon project={project} size="md" />
<span className="text-foreground truncate">{project.title}</span>
</span>
);
}