feat(timer): make pomodoro timer prominent in header with tomato styling
- Changed timer button to red/tomato themed pill button - Added red background, border, and hover effects - Shows remaining time when running - Green pulsing indicator when active - Made TomatoIcon slightly larger (22px) for visibility
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { TimerIcon, PlayIcon, PauseIcon, RefreshIcon, XIcon } from '@/components/ui/Icons';
|
||||
import { TomatoIcon, PlayIcon, PauseIcon, RefreshIcon, XIcon } from '@/components/ui/Icons';
|
||||
import { playTimerEnd } from '@/lib/sounds';
|
||||
|
||||
interface PomodoroTimerProps {
|
||||
isExpanded: boolean;
|
||||
onToggle: () => void;
|
||||
variant?: 'header' | 'floating';
|
||||
}
|
||||
|
||||
const WORK_MINUTES = 25;
|
||||
const SHORT_BREAK_MINUTES = 5;
|
||||
const LONG_BREAK_MINUTES = 15;
|
||||
|
||||
export default function PomodoroTimer({ isExpanded, onToggle }: PomodoroTimerProps) {
|
||||
export default function PomodoroTimer({ isExpanded, onToggle, variant = 'header' }: PomodoroTimerProps) {
|
||||
const [mode, setMode] = useState<'work' | 'shortBreak' | 'longBreak'>('work');
|
||||
const [timeLeft, setTimeLeft] = useState(WORK_MINUTES * 60);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
@@ -124,22 +125,56 @@ export default function PomodoroTimer({ isExpanded, onToggle }: PomodoroTimerPro
|
||||
|
||||
// Compact view (icon only)
|
||||
if (!isExpanded) {
|
||||
if (variant === 'floating') {
|
||||
// Floating Action Button for mobile
|
||||
return (
|
||||
<button
|
||||
onClick={onToggle}
|
||||
className="relative p-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 transition-colors"
|
||||
className="relative w-14 h-14 bg-indigo-600 hover:bg-indigo-700 text-white rounded-full shadow-lg flex items-center justify-center transition-all hover:scale-105 active:scale-95"
|
||||
aria-label="Open timer"
|
||||
>
|
||||
<TimerIcon size={20} />
|
||||
<TomatoIcon size={28} className="text-white" />
|
||||
{isRunning && (
|
||||
<span className="absolute top-1 right-1 w-2 h-2 bg-indigo-500 rounded-full animate-pulse" />
|
||||
<span className="absolute -top-1 -right-1 w-4 h-4 bg-green-500 rounded-full border-2 border-white dark:border-gray-900 animate-pulse" />
|
||||
)}
|
||||
{/* Time badge */}
|
||||
{isRunning && (
|
||||
<span className="absolute -bottom-1 bg-gray-900 text-white text-xs px-2 py-0.5 rounded-full">
|
||||
{Math.ceil(timeLeft / 60)}m
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// Header variant - make it pop!
|
||||
return (
|
||||
<div className="absolute right-0 top-full mt-2 w-72 bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 p-4 z-50">
|
||||
<button
|
||||
onClick={onToggle}
|
||||
className="relative flex items-center gap-2 px-3 py-2 bg-red-50 hover:bg-red-100 dark:bg-red-900/20 dark:hover:bg-red-900/30 text-red-600 dark:text-red-400 rounded-full transition-colors border border-red-200 dark:border-red-800"
|
||||
aria-label="Open timer"
|
||||
>
|
||||
<TomatoIcon size={22} />
|
||||
<span className="hidden sm:inline text-sm font-bold">Focus</span>
|
||||
{isRunning && (
|
||||
<>
|
||||
<span className="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white dark:border-gray-900 animate-pulse" />
|
||||
<span className="hidden sm:inline text-xs bg-red-100 dark:bg-red-900/40 px-2 py-0.5 rounded-full ml-1">
|
||||
{Math.ceil(timeLeft / 60)}m
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
// Expanded view - position based on variant
|
||||
const positionClasses = variant === 'floating'
|
||||
? "fixed bottom-20 right-4 sm:bottom-auto sm:top-20 sm:right-4 w-[calc(100vw-2rem)] sm:w-80"
|
||||
: "absolute right-0 top-full mt-2 w-72";
|
||||
|
||||
return (
|
||||
<div className={`${positionClasses} bg-white dark:bg-gray-800 rounded-xl shadow-xl border border-gray-200 dark:border-gray-700 p-4 z-50`}>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -1247,3 +1247,27 @@ export function ShieldIcon({ className = '', size = 24 }: IconProps) {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function TomatoIcon({ className = '', size = 24 }: IconProps) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className={className}
|
||||
>
|
||||
{/* Tomato body */}
|
||||
<ellipse cx="12" cy="14" rx="9" ry="8" />
|
||||
{/* Stem */}
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
d="M12 6 L12 3 M12 6 L9 4 M12 6 L15 4 M12 6 L10 8 M12 6 L14 8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user