You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
56 lines
2.2 KiB
56 lines
2.2 KiB
import {ReactElement, useCallback, useRef, useState} from "react";
|
|
import {ProgressBar, Stack} from "react-bootstrap";
|
|
import {useAnimationFrame} from "./AnimationHook";
|
|
import {isDefined} from "../types/type_check";
|
|
import "./TurnTimer.css";
|
|
import formatDuration from "format-duration";
|
|
|
|
export interface TurnTimerArgs {
|
|
readonly title?: string
|
|
readonly startTime?: number
|
|
readonly endTime?: number
|
|
readonly displayedTime?: number
|
|
readonly resolutionMs?: number
|
|
}
|
|
|
|
const DEFAULT_RESOLUTION_MS = 100;
|
|
|
|
export function TurnTimer({title, startTime, endTime, displayedTime, resolutionMs = DEFAULT_RESOLUTION_MS}: TurnTimerArgs): ReactElement {
|
|
const [currentTime, setCurrentTime] = useState(() => displayedTime ?? Date.now())
|
|
const accumulatedTime = useRef(0)
|
|
const animationCallback = useCallback(isDefined(displayedTime)
|
|
? () => null
|
|
: (delta: number) => {
|
|
accumulatedTime.current += delta
|
|
if (accumulatedTime.current > resolutionMs) {
|
|
accumulatedTime.current %= resolutionMs
|
|
setCurrentTime(Date.now())
|
|
}
|
|
}, [displayedTime, setCurrentTime, accumulatedTime])
|
|
useAnimationFrame(animationCallback)
|
|
|
|
if (isDefined(displayedTime) && displayedTime !== currentTime) {
|
|
setCurrentTime(displayedTime)
|
|
accumulatedTime.current = resolutionMs
|
|
}
|
|
|
|
let totalTime: number|null = null
|
|
let timeRemaining: number|null = null
|
|
let timeElapsed: number|null = null
|
|
if (isDefined(startTime)) {
|
|
if (isDefined(endTime)) {
|
|
totalTime = endTime - startTime
|
|
timeRemaining = endTime - currentTime
|
|
} else {
|
|
timeElapsed = currentTime - startTime
|
|
}
|
|
} else if (isDefined(endTime)) {
|
|
timeRemaining = endTime - currentTime
|
|
}
|
|
|
|
return <Stack className={"turnTimer"} direction="vertical">
|
|
<h2 className={"turnTimerTitle"}>{title}</h2>
|
|
{(timeRemaining !== null || timeElapsed !== null) && <div className={"turnTimerTime"}>{formatDuration(timeRemaining ?? timeElapsed ?? 0)}</div>}
|
|
{timeRemaining !== null && totalTime !== null && <ProgressBar className={"turnTimerBar"} now={timeRemaining} max={totalTime} />}
|
|
</Stack>
|
|
} |