Merge pull request #2679 from makeittech/feat/gh-2364-git-changes-count
feat(ui): show the changed-files count badge on the Git rail surface
This commit is contained in:
@@ -49,17 +49,30 @@ type RailItemProps = {
|
||||
showActivityDot: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
/** Numeric badge (e.g. the Git changed-files count); takes precedence over the activity dot. */
|
||||
badgeCount?: number | null;
|
||||
/** Accessible label that includes the badge count; falls back to `label`. */
|
||||
badgeAriaLabel?: string | null;
|
||||
/** Extra tooltip line describing the badge; rendered under the description. */
|
||||
badgeDescription?: string | null;
|
||||
orderNumber?: number | null;
|
||||
showOrderNumber?: boolean;
|
||||
onSelect: (surface: ContextSurfaceDescriptor) => void;
|
||||
};
|
||||
|
||||
// The badge corner is 16px tall; cap large counts so the pill stays compact
|
||||
// on the 36px rail button (matching the order-number badge's footprint).
|
||||
const formatRailBadgeCount = (count: number): string => (count > 99 ? '99+' : String(count));
|
||||
|
||||
const ContextPanelRailItem: React.FC<RailItemProps> = ({
|
||||
surface,
|
||||
isActive,
|
||||
showActivityDot,
|
||||
label,
|
||||
description,
|
||||
badgeCount,
|
||||
badgeAriaLabel,
|
||||
badgeDescription,
|
||||
orderNumber,
|
||||
showOrderNumber,
|
||||
onSelect,
|
||||
@@ -68,6 +81,8 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
|
||||
id: surface.id,
|
||||
});
|
||||
|
||||
const displayBadgeCount = badgeCount != null && badgeCount > 0 ? formatRailBadgeCount(badgeCount) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={setNodeRef}
|
||||
@@ -81,7 +96,7 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
onClick={() => onSelect(surface)}
|
||||
aria-label={label}
|
||||
aria-label={badgeAriaLabel ?? label}
|
||||
aria-pressed={isActive}
|
||||
className={cn(
|
||||
'flex h-9 w-9 touch-none select-none items-center justify-center rounded-md transition-colors',
|
||||
@@ -95,12 +110,6 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
|
||||
) : (
|
||||
<Icon name={surface.icon} className="h-[18px] w-[18px]" />
|
||||
)}
|
||||
{showActivityDot && !showOrderNumber ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-[var(--status-info)]"
|
||||
/>
|
||||
) : null}
|
||||
{showOrderNumber && orderNumber != null ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
@@ -108,6 +117,18 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
|
||||
>
|
||||
{orderNumber === 10 ? '0' : orderNumber}
|
||||
</span>
|
||||
) : displayBadgeCount ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="absolute right-0 top-0 flex h-4 min-w-4 items-center justify-center rounded-full bg-surface-muted px-1 text-[0.625rem] font-medium leading-none text-muted-foreground"
|
||||
>
|
||||
{displayBadgeCount}
|
||||
</span>
|
||||
) : showActivityDot ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-[var(--status-info)]"
|
||||
/>
|
||||
) : null}
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
@@ -115,6 +136,9 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span>{label}</span>
|
||||
<span className="typography-micro text-muted-foreground">{description}</span>
|
||||
{badgeDescription ? (
|
||||
<span className="typography-micro text-muted-foreground">{badgeDescription}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -258,19 +282,43 @@ export const ContextPanelRail: React.FC = () => {
|
||||
>
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<SortableContext items={surfaces.map((surface) => surface.id)} strategy={verticalListSortingStrategy}>
|
||||
{surfaces.map((surface, index) => (
|
||||
<ContextPanelRailItem
|
||||
key={surface.id}
|
||||
surface={surface}
|
||||
isActive={activeMode === surface.mode}
|
||||
showActivityDot={surface.id === 'git' && changedFilesCount > 0}
|
||||
label={t(surface.labelKey)}
|
||||
description={t(surface.descriptionKey)}
|
||||
orderNumber={index + 1}
|
||||
showOrderNumber={revealNumbers}
|
||||
onSelect={(selected) => openContextSurface(directoryKey, selected.mode)}
|
||||
/>
|
||||
))}
|
||||
{surfaces.map((surface, index) => {
|
||||
const label = t(surface.labelKey);
|
||||
// Git shows a numeric badge instead of the old activity dot.
|
||||
// Other surfaces never inherit git's changed-files signal.
|
||||
const gitChangedCount = surface.id === 'git' ? changedFilesCount : 0;
|
||||
const badgeCount = gitChangedCount > 0 ? gitChangedCount : null;
|
||||
return (
|
||||
<ContextPanelRailItem
|
||||
key={surface.id}
|
||||
surface={surface}
|
||||
isActive={activeMode === surface.mode}
|
||||
showActivityDot={false}
|
||||
label={label}
|
||||
description={t(surface.descriptionKey)}
|
||||
badgeCount={badgeCount}
|
||||
badgeAriaLabel={badgeCount !== null
|
||||
? t(
|
||||
badgeCount === 1
|
||||
? 'contextRail.surface.git.changesCountAriaSingle'
|
||||
: 'contextRail.surface.git.changesCountAriaPlural',
|
||||
{ label, count: badgeCount },
|
||||
)
|
||||
: null}
|
||||
badgeDescription={badgeCount !== null
|
||||
? t(
|
||||
badgeCount === 1
|
||||
? 'contextRail.surface.git.changesCountTooltipSingle'
|
||||
: 'contextRail.surface.git.changesCountTooltipPlural',
|
||||
{ count: badgeCount },
|
||||
)
|
||||
: null}
|
||||
orderNumber={index + 1}
|
||||
showOrderNumber={revealNumbers}
|
||||
onSelect={(selected) => openContextSurface(directoryKey, selected.mode)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</nav>
|
||||
|
||||
Reference in New Issue
Block a user