Files
kandev/apps/web/components/task/changes-panel-header.tsx
T

191 lines
5.7 KiB
TypeScript

"use client";
import {
IconCloudDownload,
IconEye,
IconChevronDown,
IconGitBranch,
IconGitCherryPick,
IconGitMerge,
IconArrowRight,
IconLoader2,
} from "@tabler/icons-react";
import { Button } from "@kandev/ui/button";
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@kandev/ui/hover-card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@kandev/ui/dropdown-menu";
import { PanelHeaderBarSplit } from "./panel-primitives";
function BranchHoverCard({
displayBranch,
baseBranchDisplay,
}: {
displayBranch: string;
baseBranchDisplay: string;
}) {
return (
<HoverCard openDelay={200} closeDelay={100}>
<HoverCardTrigger asChild>
<button
type="button"
className="flex items-center justify-center size-5 rounded hover:bg-muted/60 text-muted-foreground hover:text-foreground transition-colors cursor-default"
>
<IconGitBranch className="h-3.5 w-3.5" />
</button>
</HoverCardTrigger>
<HoverCardContent side="bottom" align="end" className="w-auto p-3">
<div className="flex flex-col gap-2.5 text-xs">
<div className="flex items-center justify-between gap-6">
<span className="text-muted-foreground/60">Your code lives in:</span>
<span className="text-muted-foreground/60">and will be merged into:</span>
</div>
<div className="flex items-center gap-2">
<span className="flex items-center gap-1.5 text-foreground font-medium">
<IconGitBranch className="h-3.5 w-3.5 text-muted-foreground" />
{displayBranch}
</span>
<div className="flex-1 border-t border-muted-foreground/20 min-w-8" />
<IconArrowRight className="h-3 w-3 text-muted-foreground/40" />
<span className="text-foreground font-medium">{baseBranchDisplay}</span>
</div>
</div>
</HoverCardContent>
</HoverCard>
);
}
function PullDropdown({
behindCount,
isLoading,
loadingOperation,
onPull,
onRebase,
}: {
behindCount: number;
isLoading: boolean;
loadingOperation: string | null;
onPull: () => void;
onRebase: () => void;
}) {
const isPulling = loadingOperation === "pull";
const isRebasing = loadingOperation === "rebase";
const isPullRelated = isPulling || isRebasing;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="sm"
variant="ghost"
className="h-5 text-[11px] px-1.5 gap-1 cursor-pointer"
disabled={isLoading}
>
{isPullRelated ? (
<IconLoader2 className="h-3 w-3 animate-spin" />
) : (
<IconCloudDownload className="h-3 w-3" />
)}
{isPulling && "Pulling…"}
{!isPulling && isRebasing && "Rebasing…"}
{!isPulling && !isRebasing && "Pull"}
{behindCount > 0 && !isPullRelated && (
<span className="text-yellow-500 text-[10px]">{behindCount}</span>
)}
{!isPullRelated && <IconChevronDown className="h-2.5 w-2.5 text-muted-foreground" />}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-44">
<DropdownMenuItem onClick={onPull} className="cursor-pointer text-xs gap-2">
<IconCloudDownload className="h-3.5 w-3.5 text-muted-foreground" />
Pull
{behindCount > 0 && (
<span className="ml-auto text-muted-foreground text-[10px]">{behindCount} behind</span>
)}
</DropdownMenuItem>
<DropdownMenuItem onClick={onRebase} className="cursor-pointer text-xs gap-2">
<IconGitCherryPick className="h-3.5 w-3.5 text-muted-foreground" />
Rebase
{behindCount > 0 && (
<span className="ml-auto text-muted-foreground text-[10px]">{behindCount} behind</span>
)}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
export function ChangesPanelHeader({
hasChanges,
hasCommits,
hasPRFiles,
displayBranch,
baseBranchDisplay,
behindCount,
isLoading,
loadingOperation,
onOpenDiffAll,
onOpenReview,
onPull,
onRebase,
}: {
hasChanges: boolean;
hasCommits: boolean;
hasPRFiles?: boolean;
displayBranch: string | null;
baseBranchDisplay: string;
behindCount: number;
isLoading: boolean;
loadingOperation: string | null;
onOpenDiffAll?: () => void;
onOpenReview?: () => void;
onPull: () => void;
onRebase: () => void;
}) {
const showDiffReview = hasChanges || hasCommits || !!hasPRFiles;
return (
<PanelHeaderBarSplit
left={
showDiffReview ? (
<>
<Button
size="sm"
variant="ghost"
className="h-5 text-[11px] px-1.5 gap-1 cursor-pointer"
onClick={onOpenDiffAll}
>
<IconGitMerge className="h-3 w-3" />
Diff
</Button>
<Button
size="sm"
variant="ghost"
className="h-5 text-[11px] px-1.5 gap-1 cursor-pointer"
onClick={onOpenReview}
>
<IconEye className="h-3 w-3" />
Review
</Button>
</>
) : undefined
}
right={
<>
{displayBranch && (
<BranchHoverCard displayBranch={displayBranch} baseBranchDisplay={baseBranchDisplay} />
)}
<PullDropdown
behindCount={behindCount}
isLoading={isLoading}
loadingOperation={loadingOperation}
onPull={onPull}
onRebase={onRebase}
/>
</>
}
/>
);
}