SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
1.8 KB · 48 lines tsx
Raw Blame History
1import * as React from "react";2import { cn } from "@/lib/utils";34/**5 * Linear meter for quota-style values (used vs limit). Color only turns to warning/danger when6 * the threshold is actually crossed; the label always carries the numbers.7 */8export function QuotaBar({9  label,10  used,11  limit,12  usedLabel,13  limitLabel,14  unlimited,15  hint,16  className,17}: {18  label: React.ReactNode;19  used: number;20  limit: number | null;21  usedLabel: string;22  limitLabel?: string;23  unlimited?: boolean;24  hint?: React.ReactNode;25  className?: string;26}) {27  const pct = unlimited || !limit || limit <= 0 ? 0 : Math.min(100, (used / limit) * 100);28  const tone = pct >= 100 ? "bg-danger" : pct >= 80 ? "bg-warning" : "bg-accent";29  return (30    <div className={cn("flex flex-col gap-1.5", className)}>31      <div className="flex items-baseline justify-between gap-3 text-[13px]">32        <span className="font-medium">{label}</span>33        <span className="font-mono tabular text-fg-muted">34          <span className="text-fg">{usedLabel}</span>35          {unlimited ? <span className="text-fg-subtle"> · unlimited</span> : limitLabel ? <span> / {limitLabel}</span> : null}36        </span>37      </div>38      <div className="h-1.5 w-full overflow-hidden rounded-full bg-bg-muted" role="progressbar" aria-valuemin={0} aria-valuemax={100} aria-valuenow={Math.round(pct)} aria-label={typeof label === "string" ? label : undefined}>39        <div className={cn("h-full rounded-full transition-[width]", tone)} style={{ width: `${unlimited ? 0 : pct}%` }} />40      </div>41      <div className="flex items-center justify-between text-[11.5px] text-fg-subtle">42        <span>{hint}</span>43        {!unlimited && limit ? <span className="font-mono tabular">{pct.toFixed(pct > 0 && pct < 1 ? 1 : 0)}%</span> : null}44      </div>45    </div>46  );47}48