Untitled
import { Clock } from "lucide-react"; import React from "react"; import { AnimatePresence, motion } from "framer-motion"; const TimePicker = () => { const [timeInput, setTimeInput] = React.useState( new Date().toLocaleTimeString("en-US", { hour12: true }).slice(0, 8) ); const [suffix, setSuffix] = React.useState<"AM" | "PM">( new Date().getHours() >= 12 ? "PM" : "AM" ); const [hour, setHour] = React.useState(new Date().getHours() % 12 || 12); const [minute, setMinute] = React.useState(new Date().getMinutes()); const [second, setSecond] = React.useState(new Date().getSeconds()); const [showSelector, setShowSelector] = React.useState(false); const formatTimeInput = (value: string): string => { const digitsOnly = value.replace(/[^0-9]/g, ""); const segments = []; for (let i = 0; i < digitsOnly.length; i += 2) { segments.push(digitsOnly.slice(i, i + 2)); } return segments.join(":").slice(0, 8); }; const validateTime = (value: string): boolean => { const regex = /^([0-9]{1,2}):([0-5][0-9]):([0-5][0-9])$/; // Regex for HH:MM:SS format if (!regex.test(value)) return false; const [h, m, s] = value.split(":").map(Number); // Check hour, minute, and second constraints if (h < 1 || h > 12 || m < 0 || m > 59 || s < 0 || s > 59) { return false; } return true; }; const changeTime = (e: React.ChangeEvent<HTMLInputElement>) => { const value = formatTimeInput(e.target.value); setTimeInput(value); if (validateTime(value)) { const [h, m, s] = value.split(":").map(Number); setHour(h); setMinute(m); setSecond(s); setSuffix(h >= 12 ? "PM" : "AM"); } }; const toggleSuffix = () => { const newSuffix = suffix === "AM" ? "PM" : "AM"; setSuffix(newSuffix); const updatedHour = (hour % 12) + (newSuffix === "PM" ? 12 : 0); setHour(updatedHour); setTimeInput( `${String(updatedHour % 12 || 12).padStart(2, "0")}:${String(minute).padStart(2, "0")}:${String(second).padStart(2, "0")}` ); }; const generateValues = (start: number, end: number) => Array.from({ length: end - start + 1 }, (_, i) => i + start); const toggleSelector = () => setShowSelector(!showSelector); return ( <div className="flex flex-col space-y-4 border border-gray-300 rounded-md px-2 py-1 m-4"> <div className="flex items-center space-x-2 justify-between"> <input aria-label="Time" type="text" value={timeInput} onChange={changeTime} maxLength={8} className="flex-grow rounded-md px-2 py-1 text-sm outline-none border-b" /> <div className="flex items-center space-x-2"> <button onClick={toggleSuffix} className="text-sm font-semibold border px-2 py-1 rounded-md" > {suffix} </button> <button aria-label="Toggle clock" onClick={toggleSelector} className="flex items-center justify-center w-8 h-8 border rounded-md" > <Clock size={16} /> </button> </div> </div> <AnimatePresence> {showSelector && ( <motion.div initial={{ opacity: 0, y: -10 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -10 }} className="relative" > <div className="grid grid-cols-3 gap-2"> <p className="mb-2 text-sm font-semibold sticky top-0 bg-white py-1"> Hours </p> <p className="mb-2 text-sm font-semibold sticky top-0 bg-white py-2"> Minutes </p> <p className="mb-2 text-sm font-semibold sticky top-0 bg-white py-2"> Seconds </p> </div> <div className="grid grid-cols-3 gap-4 h-96 overflow-y-scroll"> <div> <div className="grid grid-cols-1 gap-2"> {generateValues(1, 12).map((h) => ( <button key={h} className={`px-3 py-1 text-sm border rounded-md ${ h === hour ? "bg-blue-500 text-white" : "bg-gray-100" }`} onClick={() => setHour(h)} > {String(h).padStart(2, "0")} </button> ))} </div> </div> <div> <div className="grid grid-cols-1 gap-2"> {generateValues(0, 59).map((m) => ( <button key={m} className={`px-3 py-1 text-sm border rounded-md ${ m === minute ? "bg-blue-500 text-white" : "bg-gray-100" }`} onClick={() => setMinute(m)} > {String(m).padStart(2, "0")} </button> ))} </div> </div> <div> <div className="grid grid-cols-1 gap-2"> {generateValues(0, 59).map((s) => ( <button key={s} className={`px-3 py-1 text-sm border rounded-md ${ s === second ? "bg-blue-500 text-white" : "bg-gray-100" }`} onClick={() => setSecond(s)} > {String(s).padStart(2, "0")} </button> ))} </div> </div> </div> </motion.div> )} </AnimatePresence> </div> ); }; export default TimePicker;
Leave a Comment