Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 76 additions & 51 deletions apps/web/src/components/ThreadStatusIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
visibleThreadPullRequests,
type ThreadPullRequestBadge,
} from "@t3tools/shared/threadPullRequests";
import { type MouseEvent } from "react";
import { type MouseEvent, type ReactNode } from "react";
import { buttonVariants, InlineButton } from "./ui/button";
import { cn } from "../lib/utils";

Expand Down Expand Up @@ -223,7 +223,6 @@ export function ThreadPullRequestBadgeControl({
const presentation = resolveThreadPullRequestBadgePresentation({ badge, number, url, status });
if (presentation === null) return null;
const isStack = badge?.kind === "stack";
const showList = isStack || (badge?.kind === "pull-request" && badge.others > 0);
const className = cn(
variant === "ghost"
? buttonVariants({ variant: "ghost", size: "xs" })
Expand Down Expand Up @@ -271,21 +270,26 @@ export function ThreadPullRequestBadgeControl({
</TooltipTrigger>
<TooltipPopup
side="top"
variant={showList ? "glass" : "default"}
className={
showList
? "pointer-events-auto w-80 max-w-[calc(100vw-2rem)] text-left whitespace-normal"
: undefined
}
sideOffset={0}
variant="glass"
className="pointer-events-auto w-80 max-w-[calc(100vw-2rem)] text-left whitespace-normal"
>
{showList ? (
{visibleThreadPullRequests(pullRequests).length > 0 ? (
<ThreadPullRequestsMiniList
pullRequests={pullRequests}
onOpenPullRequest={onOpenPullRequest}
/>
) : (
presentation.label
)}
) : number !== undefined && url !== undefined ? (
<ul className="flex flex-col gap-1">
<ThreadPullRequestMiniListItem
number={number}
url={url}
title={status?.tooltipTitle ?? presentation.label}
presentation={presentation}
onOpenPullRequest={onOpenPullRequest}
/>
</ul>
) : null}
</TooltipPopup>
</Tooltip>
);
Expand Down Expand Up @@ -316,56 +320,77 @@ export function ThreadPullRequestsMiniList({
snapshot === null
? null
: resolvePullRequestState({ state: snapshot.state, isDraft: snapshot.isDraft });
const content = (
<>
{presentation ? (
<presentation.Icon
aria-hidden
className={cn("size-3 shrink-0", presentation.toneClassName)}
/>
) : (
<PullRequestGlyph.pullRequest
aria-hidden
className="size-3 shrink-0 stroke-muted-foreground"
/>
)}
<span className="shrink-0 font-mono tabular-nums">#{line.link.number}</span>
<span className="min-w-0 truncate text-foreground/75">
{snapshot?.title ?? line.link.repository}
</span>
return (
<ThreadPullRequestMiniListItem
key={`${line.link.host}/${line.link.repository}#${line.link.number}`}
number={line.link.number}
url={line.link.url}
title={snapshot?.title ?? line.link.repository}
presentation={presentation}
depth={line.depth}
onOpenPullRequest={onOpenPullRequest}
>
{line.stack ? (
<span className="ml-auto shrink-0 pl-1 text-[10px]">
{line.stack.kind === "native" ? "stack" : "chain"} · {line.stack.size}
</span>
) : null}
</>
);
return (
<li
key={`${line.link.host}/${line.link.repository}#${line.link.number}`}
style={{ paddingLeft: `${Math.min(line.depth, 3) * 0.75}rem` }}
>
{onOpenPullRequest ? (
<a
href={line.link.url}
target="_blank"
rel="noopener noreferrer"
className="flex min-w-0 items-center gap-2 rounded-sm px-1 py-1 hover:bg-accent focus-visible:bg-accent focus-visible:outline-2 focus-visible:outline-ring"
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => onOpenPullRequest(event, line.link.url)}
>
{content}
</a>
) : (
<div className="flex min-w-0 items-center gap-2">{content}</div>
)}
</li>
</ThreadPullRequestMiniListItem>
);
})}
</ul>
);
}

function ThreadPullRequestMiniListItem({
number,
url,
title,
presentation,
depth = 0,
onOpenPullRequest,
children,
}: {
number: number;
url: string;
title: string;
presentation: Pick<ThreadPullRequestBadgePresentation, "Icon" | "toneClassName"> | null;
depth?: number;
onOpenPullRequest?: ((event: MouseEvent<HTMLAnchorElement>, url: string) => void) | undefined;
children?: ReactNode;
}) {
const Icon = presentation?.Icon ?? PullRequestGlyph.pullRequest;
const content = (
<>
<Icon
aria-hidden
className={cn("size-3 shrink-0", presentation?.toneClassName ?? "stroke-muted-foreground")}
/>
<span className="shrink-0 font-mono tabular-nums">#{number}</span>
<span className="min-w-0 truncate text-foreground/75">{title}</span>
{children}
</>
);
return (
<li style={{ paddingLeft: `${Math.min(depth, 3) * 0.75}rem` }}>
{onOpenPullRequest ? (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="flex min-w-0 items-center gap-2 rounded-sm px-1 py-1 hover:bg-accent focus-visible:bg-accent focus-visible:outline-2 focus-visible:outline-ring"
onPointerDown={(event) => event.stopPropagation()}
onClick={(event) => onOpenPullRequest(event, url)}
>
{content}
</a>
) : (
<div className="flex min-w-0 items-center gap-2">{content}</div>
)}
</li>
);
}

export function prStatusIndicator(
pr: ThreadPr,
provider: VcsStatusResult["sourceControlProvider"] | null | undefined,
Expand Down
Loading