Files
RasadDam_Frontend/src/components/time-picker/CustomTimePicker.tsx

149 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-01-19 13:08:58 +03:30
import React, { useEffect, useRef, useState } from "react";
import {
bgInputPrimaryColor,
mobileBorders,
textColor,
} from "../../data/getColorBasedOnMode";
import { getSizeStyles } from "../../data/getInputSizes";
import { checkIsMobile } from "../../utils/checkIsMobile";
interface TimePickerProps {
value?: string;
onChange?: (value: string) => void;
label?: string;
disabled?: boolean;
className?: string;
size?: "small" | "medium" | "large";
}
const TimePicker: React.FC<TimePickerProps> = ({
onChange,
label = "ساعت",
disabled = false,
className = "",
size = "medium",
}) => {
const pickerRef = useRef<HTMLDivElement>(null!);
const hourRef = useRef<HTMLDivElement>(null!);
const minuteRef = useRef<HTMLDivElement>(null!);
const [isOpen, setIsOpen] = useState(false);
const now = new Date();
const initialHour = now.getHours();
const initialMinute = now.getMinutes();
const [selectedHour, setSelectedHour] = useState<number>(initialHour);
const [selectedMinute, setSelectedMinute] = useState<number>(initialMinute);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
pickerRef.current &&
!pickerRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
useEffect(() => {
const timeString = `${String(selectedHour).padStart(2, "0")}:${String(
selectedMinute
).padStart(2, "0")}:00`;
if (onChange) {
onChange(timeString);
}
}, [selectedHour, selectedMinute, onChange]);
const scrollToSelected = (
ref: React.RefObject<HTMLDivElement>,
selected: number
) => {
const el = ref.current?.querySelector(`[data-value="${selected}"]`);
el?.scrollIntoView({ behavior: "auto", block: "center" });
};
useEffect(() => {
if (isOpen) {
scrollToSelected(hourRef, selectedHour);
scrollToSelected(minuteRef, selectedMinute);
}
}, [isOpen]);
const renderColumn = (
title: string,
range: number,
selected: number,
setSelected: (val: number) => void,
ref: React.RefObject<HTMLDivElement>
) => (
<div className="h-60 w-30 flex flex-col items-center">
<div className={`py-1 text-xs font-semibold ${textColor}`}>{title}</div>
<div
ref={ref}
className="flex-1 overflow-x-hidden w-full overflow-y-auto overflow-hidden xs:scrollbar-hide text-center"
>
<ul className="space-y-1">
{Array.from({ length: range }, (_, i) => (
<li
key={i}
data-value={i}
className={`py-2 cursor-pointer ${
i === selected
? "text-amber-900 dark:text-amber-200 font-bold scale-105"
: textColor
}`}
onClick={() => setSelected(i)}
>
{String(i).padStart(2, "0")}
</li>
))}
</ul>
</div>
</div>
);
return (
<div
className={`relative w-28 justify-center ${className}`}
ref={pickerRef}
>
<div className="flex items-center gap-2">
<label className={`text-sm ${textColor}`}>{label}</label>
<input
readOnly
value={`${String(selectedHour).padStart(2, "0")}:${String(
selectedMinute
).padStart(2, "0")}`}
onClick={() => setIsOpen(!isOpen)}
className={`w-full border rounded-xl bg-white text-sm text-center text-dark-800 shadow-sm focus:border-blue-400 focus:ring focus:ring-blue-200 ${
checkIsMobile() && mobileBorders
} ${getSizeStyles(size).padding} ${bgInputPrimaryColor} ${textColor}`}
disabled={disabled}
/>
</div>
{isOpen && (
<div
className={`absolute z-50 mt-2 flex rounded-lg shadow-lg text-white text-sm w-40 ${bgInputPrimaryColor}`}
>
{renderColumn(
"دقیقه",
60,
selectedMinute,
setSelectedMinute,
minuteRef
)}
{renderColumn("ساعت", 24, selectedHour, setSelectedHour, hourRef)}
</div>
)}
</div>
);
};
export default TimePicker;