Organize/components/board/todo-progress-pie.tsx

68 lines
3.0 KiB
XML

"use client";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
/**
* Small pie-chart badge pinned to a Group card's bottom-right corner,
* showing what fraction of its to-dos are complete. `completed`/`total`
* come straight from the group's own to-do list, so it's recalculated and
* redrawn on every render -- any add/toggle/delete that changes those
* numbers repaints this automatically, with no separate update path to
* keep in sync.
*/
export function TodoProgressPie({
completed,
total,
accentColor,
}: {
completed: number;
total: number;
accentColor: string;
}) {
if (total === 0) return null;
const pct = Math.round((completed / total) * 100);
// Same hue as the stroke and the "completed" wedge, just faded into the
// card surface -- so the remaining wedge reads as an absence of
// progress rather than a second, competing color.
const emptyColor = `color-mix(in srgb, ${accentColor} 22%, var(--card))`;
return (
<Tooltip>
<TooltipTrigger
render={
<div
role="img"
aria-label={`${pct}% of to-dos completed`}
tabIndex={0}
// z-10: this badge pokes outside its own card's bottom-right
// corner, into the gap where the *next* card down starts. That
// next card is a later DOM sibling with its own stacking
// context (see hover:-translate-y-0.5 in GroupCard), so
// without an explicit z-index here it paints on top and crops
// the badge's overlapping edge.
// -right-2.5: the badge's horizontal position is a budget, not
// a taste call. From the card's right border there are exactly
// 10px of lane (CategoryLane's px-2.5) before the lane's own
// clip edge -- or its vertical scrollbar, in classic-scrollbar
// browsers, which sits at the same 10px mark and paints over
// anything past it. So the badge may stick out at most ~7px
// (10px offset from the card's *padding* edge minus the 3px
// border) to stay clear of both. In exchange the "N/N done"
// footer text carries pr-2.5, which hands this badge the room
// it needs to clear the text (see the span in GroupCard).
// Moving it further out looks like it "fits" until it doesn't:
// past the 10px mark the lane clips the badge's edge or grows
// a stray horizontal scrollbar.
className="absolute -right-2.5 -bottom-1.5 z-10 size-7 shrink-0 cursor-default rounded-full border-2 shadow-sm outline-none transition-transform hover:scale-110 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1"
style={{
borderColor: accentColor,
background: `conic-gradient(${accentColor} ${pct}%, ${emptyColor} ${pct}% 100%)`,
}}
/>
}
/>
<TooltipContent side="top">{pct}% To-Dos Completed</TooltipContent>
</Tooltip>
);
}