Untitled
unknown
plain_text
9 months ago
6.0 kB
5
Indexable
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;
Editor is loading...
Leave a Comment