68 lines
3.0 KiB
XML
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>
|
|
);
|
|
}
|