feat(projects): switch project list view control to a dropdown menu (MUL-5030) (#5677)

Align the project list view control with the issue list: instead of a
button that flips table <-> cards on click, the trigger now opens a
dropdown menu with a 'View' header and a radio group of view modes
(Table / Cards). Using DropdownMenuRadioGroup keeps it trivial to add
future view modes as new menu items.

Co-authored-by: Bohan-J <bohan@devv.ai>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Bohan Jiang
2026-07-20 16:37:30 +08:00
committed by GitHub
parent c2df5d786d
commit a56f41cf03
2 changed files with 58 additions and 26 deletions

View File

@@ -112,6 +112,12 @@ vi.mock("@multica/ui/components/ui/dropdown-menu", () => ({
DropdownMenuContent: ({ children }: { children: React.ReactNode }) => (
<div>{children}</div>
),
DropdownMenuGroup: ({ children }: { children: React.ReactNode }) => (
<div>{children}</div>
),
DropdownMenuLabel: ({ children }: { children: React.ReactNode }) => (
<div>{children}</div>
),
DropdownMenuItem: ({
children,
onClick,

View File

@@ -27,6 +27,7 @@ import {
type ProjectColumnKey,
type ProjectListFilters,
type ProjectSortField,
type ProjectViewMode,
} from "@multica/core/projects";
import {
pinListOptions,
@@ -58,7 +59,9 @@ import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
@@ -1150,32 +1153,55 @@ export function ProjectsPage() {
</PopoverContent>
</Popover>
{/* View toggle — a single button that flips table ⇄ cards.
Pure presentation; coupled to nothing else. */}
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size="sm"
className="h-8 w-8 gap-1 px-0 text-muted-foreground md:w-auto md:px-2.5"
onClick={() => setViewMode(isCompact ? "comfortable" : "compact")}
>
{isCompact ? (
<Rows3 className="size-3.5" />
) : (
<LayoutGrid className="size-3.5" />
)}
<span className="hidden md:inline">
{isCompact ? t(($) => $.page.view_table) : t(($) => $.page.view_cards)}
</span>
</Button>
}
/>
<TooltipContent side="bottom">
{isCompact ? t(($) => $.page.view_cards) : t(($) => $.page.view_table)}
</TooltipContent>
</Tooltip>
{/* View selector — a dropdown menu to pick the list view,
aligned with the issue list's view menu. The trigger shows
the active view; the menu carries every mode so new views
can be added as menu items. Pure presentation. */}
<DropdownMenu>
<Tooltip>
<DropdownMenuTrigger
render={
<TooltipTrigger
render={
<Button
variant="outline"
size="sm"
className="h-8 w-8 gap-1 px-0 text-muted-foreground md:w-auto md:px-2.5"
>
{isCompact ? (
<Rows3 className="size-3.5" />
) : (
<LayoutGrid className="size-3.5" />
)}
<span className="hidden md:inline">
{isCompact ? t(($) => $.page.view_table) : t(($) => $.page.view_cards)}
</span>
</Button>
}
/>
}
/>
<TooltipContent side="bottom">{t(($) => $.toolbar.view)}</TooltipContent>
</Tooltip>
<DropdownMenuContent align="end" className="w-auto">
<DropdownMenuGroup>
<DropdownMenuLabel>{t(($) => $.toolbar.view)}</DropdownMenuLabel>
</DropdownMenuGroup>
<DropdownMenuRadioGroup
value={viewMode}
onValueChange={(v) => setViewMode(v as ProjectViewMode)}
>
<DropdownMenuRadioItem value="compact">
<Rows3 />
{t(($) => $.page.view_table)}
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="comfortable">
<LayoutGrid />
{t(($) => $.page.view_cards)}
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>