So I decided to try and do my own time picker component. Why? Well there isn't a single UI library out there that the user can swipe up or down to choose just hour or minutes or PM/AM. I don't need the user to select a date, I just want the time they want.
So far, I can scroll and make the user choose the numbers on swipe both up/down. The problem is im trying that the user can move multiple numbers on the swiper instead of having to swipe and only move 1 number up or down. I also wanted to see the numbers moving as the user swipes.
If I can't do this I will simply to an select tag with options, but I wanted to avoid this as I don't like how it looks for mobile devices as they use the native select.
This is the first time trying to something like this so I tried to use chatGPT for this as well, so don't think I did this from scratch lol. I haven't had to do something custom like this before.
"use client";
import React, { useState } from "react";
import TimeSection from "./TimeSection";
export default function TimePickerModal() {
const [isOpen, setIsOpen] = useState(false);
const [selectedHour, setSelectedHour] = useState(12);
const [selectedMinute, setSelectedMinute] = useState(30);
const hours = Array.from({ length: 24 }, (_, i) => i);
const minutes = Array.from({ length: 60 }, (_, i) => i);
const handleSwipe = (
setter: React.Dispatch<React.SetStateAction<number>>,
values: number[],
currentValue: number
) => {
let startY: number | null = null;
let initialValue: number | null = null;
return (e: React.TouchEvent<HTMLDivElement>) => {
if (e.type === "touchstart" && e.touches.length > 0) {
startY = e.touches[0].clientY;
initialValue = currentValue;
} else if (
e.type === "touchmove" &&
startY !== null &&
initialValue !== null
) {
const currentY = e.touches[0].clientY;
const diff = currentY - startY;
const swipeSensitivity = 50;
const unitsToMove = Math.round(diff / swipeSensitivity);
const newValue = initialValue - unitsToMove;
if (newValue >= 0 && newValue < values.length) {
setter(newValue);
} else if (newValue < 0) {
setter(0);
} else if (newValue >= values.length) {
setter(values.length - 1);
}
}
};
};
return (
<div>
<button onClick={() => setIsOpen(true)}>Open Time Picker</button>
{isOpen && (
<div
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0,0,0,0.5)",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<div
style={{
backgroundColor: "white",
padding: "20px",
borderRadius: "5px",
display: "flex",
flexDirection: "row",
color: "black",
}}
>
<TimeSection
values={hours}
currentValue={selectedHour}
handleSwipe={handleSwipe(setSelectedHour, hours, selectedHour)}
/>
<TimeSection
values={minutes}
currentValue={selectedMinute}
handleSwipe={handleSwipe(
setSelectedMinute,
minutes,
selectedMinute
)}
/>
<button
onClick={() => {
console.log(
`Selected Time: ${String(selectedHour).padStart(
2,
"0"
)}:${String(selectedMinute).padStart(2, "0")}`
);
setIsOpen(false);
}}
>
Enter
</button>
</div>
</div>
)}
</div>
);
}
TimeSection.tsx
import React from "react";
interface TimeSectionProps {
values: number[];
currentValue: number;
handleSwipe: (e: React.TouchEvent<HTMLDivElement>) => void;
}
export default function TimeSection({
values,
currentValue,
handleSwipe,
}: TimeSectionProps) {
return (
<div
style={{
height: "250px",
width: "50px",
overflow: "hidden",
position: "relative",
}}
>
<div
onTouchStart={handleSwipe}
onTouchMove={handleSwipe}
style={{
position: "absolute",
top: `calc(50% - ${currentValue * 24}px)`,
width: "100%",
textAlign: "center",
}}
>
{values.map((value, index) => (
<div
key={index}
style={{
color: "black",
height: "24px",
lineHeight: "24px",
visibility:
index >= currentValue - 2 && index <= currentValue + 2
? "visible"
: "hidden",
opacity: index === currentValue ? 1 : 0.5,
fontSize: index === currentValue ? "24px" : "18px",
}}
>
{String(value).padStart(2, "0")}
</div>
))}
</div>
</div>
);
}
Anyone know how can I fix or if there is a hidden npm package idk? Hopefully not, but if I don't find a solution then I will stick with select and option tags.
AntD component supports many components including timepicker. https://ant.design/components/time-picker
if you check this, you can find the time-picker, which you can only select time.