Files
viberoom-web/src/widgets/space-timer-hud/ui/SpaceTimerHudWidget.tsx

142 lines
4.9 KiB
TypeScript

'use client';
import { copy } from '@/shared/i18n';
import { cn } from '@/shared/lib/cn';
import {
RECOVERY_30S_MODE_LABEL,
Restart30sAction,
useRestart30s,
} from '@/features/restart-30s';
interface SpaceTimerHudWidgetProps {
timeDisplay?: string;
className?: string;
hasActiveSession?: boolean;
sessionPhase?: 'focus' | 'break' | null;
playbackState?: 'running' | 'paused' | null;
isControlsDisabled?: boolean;
isImmersionMode?: boolean;
canStart?: boolean;
canPause?: boolean;
canReset?: boolean;
onStartClick?: () => void;
onPauseClick?: () => void;
onResetClick?: () => void;
}
const HUD_ACTIONS = copy.space.timerHud.actions;
export const SpaceTimerHudWidget = ({
timeDisplay = '25:00',
className,
hasActiveSession = false,
sessionPhase = 'focus',
playbackState = 'paused',
isControlsDisabled = false,
isImmersionMode = false,
canStart = true,
canPause = false,
canReset = false,
onStartClick,
onPauseClick,
onResetClick,
}: SpaceTimerHudWidgetProps) => {
const { isBreatheMode, triggerRestart } = useRestart30s();
const modeLabel = isBreatheMode
? RECOVERY_30S_MODE_LABEL
: !hasActiveSession
? copy.space.timerHud.readyMode
: sessionPhase === 'break'
? copy.space.timerHud.breakMode
: copy.space.timerHud.focusMode;
return (
<div
className={cn(
'pointer-events-none fixed inset-x-0 z-20 flex justify-center px-4 sm:px-6',
className,
)}
style={{ bottom: 'calc(env(safe-area-inset-bottom, 0px) + 2rem)' }}
>
<div className="relative pointer-events-auto">
<section
className={cn(
'relative z-10 flex h-[3.5rem] items-center justify-between gap-6 overflow-hidden rounded-full px-5 transition-colors',
isImmersionMode
? 'border border-white/10 bg-black/20 backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.12)]'
: 'border border-white/15 bg-black/30 backdrop-blur-2xl shadow-[0_8px_32px_rgba(0,0,0,0.16)]',
)}
>
<div className="flex items-center gap-3">
<span
className={cn(
'w-14 text-right text-[10px] font-bold uppercase tracking-[0.15em] opacity-80',
sessionPhase === 'break' ? 'text-emerald-400' : 'text-brand-primary'
)}
>
{modeLabel}
</span>
<span className="w-[1px] h-4 bg-white/10" />
<span
className={cn(
'w-20 text-[1.4rem] font-medium tracking-tight text-center',
isImmersionMode ? 'text-white/90' : 'text-white',
)}
>
{timeDisplay}
</span>
</div>
<div className="flex items-center gap-1.5 pl-2 border-l border-white/10">
{HUD_ACTIONS.map((action) => {
const isStartAction = action.id === 'start';
const isPauseAction = action.id === 'pause';
const isResetAction = action.id === 'reset';
const isDisabled =
isControlsDisabled ||
(isStartAction ? !canStart : isPauseAction ? !canPause : !canReset);
const isHighlighted =
(isStartAction && playbackState !== 'running') ||
(isPauseAction && playbackState === 'running');
return (
<button
key={action.id}
type="button"
title={action.label}
aria-pressed={isHighlighted}
disabled={isDisabled}
onClick={() => {
if (isStartAction) onStartClick?.();
if (isPauseAction) onPauseClick?.();
if (isResetAction) onResetClick?.();
}}
className={cn(
'inline-flex h-8 w-8 items-center justify-center rounded-full text-sm transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 active:scale-95 disabled:cursor-not-allowed disabled:opacity-30',
isImmersionMode
? 'text-white/70 hover:bg-white/10 hover:text-white'
: 'text-white/80 hover:bg-white/15 hover:text-white',
isStartAction && isHighlighted
? 'bg-white/10 text-white shadow-sm'
: '',
isPauseAction && isHighlighted
? 'bg-white/10 text-white shadow-sm'
: '',
)}
>
<span aria-hidden>{action.icon}</span>
<span className="sr-only">{action.label}</span>
</button>
);
})}
<Restart30sAction
onTrigger={triggerRestart}
className="h-8 w-8 ml-1"
/>
</div>
</section>
</div>
</div>
);
};