34 lines
1.2 KiB
TypeScript
34 lines
1.2 KiB
TypeScript
export interface ExperienceBarProps {
|
|
level: number;
|
|
current: number;
|
|
required: number;
|
|
}
|
|
|
|
function experiencePercent(current: number, required: number): number {
|
|
if (!Number.isFinite(current) || !Number.isFinite(required) || required <= 0) return 100;
|
|
return Math.max(0, Math.min(100, (current / required) * 100));
|
|
}
|
|
|
|
export function ExperienceBar({ level, current, required }: ExperienceBarProps) {
|
|
const atCap = required <= 0;
|
|
const percent = experiencePercent(current, required);
|
|
return (
|
|
<section className="experience-bar" aria-label={`Level ${level} experience`}>
|
|
<div
|
|
className="experience-bar__track"
|
|
role="progressbar"
|
|
aria-label={atCap ? "Maximum level" : `Experience: ${Math.round(current)} of ${Math.round(required)}`}
|
|
aria-valuemin={0}
|
|
aria-valuemax={atCap ? 1 : Math.max(1, Math.round(required))}
|
|
aria-valuenow={atCap ? 1 : Math.max(0, Math.round(current))}
|
|
>
|
|
<span style={{ width: `${percent}%` }} />
|
|
</div>
|
|
<div className="experience-bar__label">
|
|
<strong>Level {level}</strong>
|
|
<span>{atCap ? "Maximum level" : `${Math.round(current)} / ${Math.round(required)} XP`}</span>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|