Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/lib/utils.check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/** Self-check for `formatMinutesSeconds`, which every playback time in the app goes through. */
export {};

import { formatMinutesSeconds } from "./utils";

function check(actual: string, expected: string, message: string): void {
if (actual !== expected) throw new Error(`FAILED: ${message} (got ${actual}, want ${expected})`);
}

check(formatMinutesSeconds(225), "3:45", "under an hour stays m:ss, no leading zero");
check(formatMinutesSeconds(59.9), "0:59", "fractions floor");
check(formatMinutesSeconds(3725), "1:02:05", "an hour and up adds hours, padding minutes");
check(formatMinutesSeconds(-1), "0:00", "a countdown that overshoots clamps to zero");
check(formatMinutesSeconds(Number.NaN), "0:00", "an unknown duration reads as zero");

console.log("formatMinutesSeconds: ok");
13 changes: 8 additions & 5 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ export function cn(...inputs: ClassValue[]) {
}

/**
* `m:ss` for a whole-second duration. Callers round `seconds` themselves (floor for elapsed
* position, ceil for a countdown) — this only formats what it's given.
* `m:ss` for a whole-second duration, `h:mm:ss` from an hour up. Callers round `seconds`
* themselves (floor for elapsed position, ceil for a countdown) — this only formats what it's given.
*/
export function formatMinutesSeconds(seconds: number): string {
if (!Number.isFinite(seconds)) return "0:00";
const whole = Math.max(0, Math.floor(seconds));
const mins = Math.floor(whole / 60);
const secs = whole % 60;
return `${mins}:${secs.toString().padStart(2, "0")}`;
const hours = Math.floor(whole / 3600);
const mins = Math.floor((whole % 3600) / 60);
const secs = (whole % 60).toString().padStart(2, "0");
return hours > 0
? `${hours}:${mins.toString().padStart(2, "0")}:${secs}`
: `${mins}:${secs}`;
}
4 changes: 2 additions & 2 deletions src/ui/components/player/SeekBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export function SeekBar() {

return (
<div className="group/seek flex w-full items-center gap-2.5">
<span className="w-10 shrink-0 text-right text-xs tabular-nums text-muted-foreground">
<span className="min-w-10 shrink-0 text-right text-xs tabular-nums text-muted-foreground">
{formatTime(currentTime)}
</span>
<input
Expand All @@ -266,7 +266,7 @@ export function SeekBar() {
} as React.CSSProperties}
aria-label="Seek"
/>
<span className="w-10 shrink-0 text-xs tabular-nums text-muted-foreground">
<span className="min-w-10 shrink-0 text-xs tabular-nums text-muted-foreground">
{formatTime(duration)}
</span>
</div>
Expand Down
Loading