first commit
This commit is contained in:
148
src/components/time-picker/CustomTimePicker.tsx
Normal file
148
src/components/time-picker/CustomTimePicker.tsx
Normal file
@@ -0,0 +1,148 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user