DEV Community

Cover image for Build a custom time picker React component
Anlisha Maharjan
Anlisha Maharjan

Posted on • Originally published at anlisha.com.np on

3 2

Build a custom time picker React component

In this article, we’ll create a custom time picker component combining react-datetime and react-input-mask packages.

Step1 — Start Basic CRA

npx create-react-app axon
Enter fullscreen mode Exit fullscreen mode

Step2 — Install react-datetime and react-input-mask packages

cd axon
npm i react-datetime react-input-mask moment
Enter fullscreen mode Exit fullscreen mode

Step3 — Setup time picker component

import React, { useState } from "react";
import Datetime from "react-datetime";
import InputMask from "react-input-mask";
import moment from "moment";
const CustomTimePicker = ({ label, classes, ...props }) => {
const [selectedTime, setSelectedTime] = useState("00:00");
const [formatChars] = useState({
1: "[0-2]",
2: "[0-9]",
3: "[0-5]",
4: "[0-9]",
5: "[0-5]",
6: "[0-9]",
});
const handleTimeChange = (dateString) => {
if (dateString) {
setSelectedTime(moment(dateString, "HH:mm"));
} else {
setSelectedTime(null);
}
};
const beforeMaskedValueChange = (newState) => {
const { value } = newState;
// Conditional mask for the 2nd digit base on the first digit
if (value.startsWith("2")) formatChars["2"] = "[0-3]";
// To block 24, 25, etc.
else formatChars["2"] = "[0-9]"; // To allow 05, 12, etc.
return { value, selection: newState.selection };
};
const maskInput = (props) => {
return (
<>
<InputMask
className="field"
mask="12:34"
maskchar="0"
formatChars={formatChars}
beforeMaskedValueChange={beforeMaskedValueChange}
{...props}
/>
</>
);
};
return (
<div className={`field-group mb-0`}>
<label htmlFor="" className={`field-label`}>
{label}
</label>
<div className={`field-wrap icon-end kc-timepicker outlined`}>
<Datetime
value={selectedTime}
open={true}
dateFormat={false}
timeFormat={"HH:mm"}
renderInput={maskInput}
inputProps={{
className: `field ${classes?.input || ""}`,
}}
onChange={handleTimeChange}
/>
</div>
</div>
);
};
export default CustomTimePicker;

Step4 — Style time picker component

* {
box-sizing: border-box;
}
html,
body {
margin: 0;
}
/* ##### Custom Timepicker InputMask & ReactDateTime ##### */
$inputBorderColor: #d9dce4;
$pickerFieldHeight: 5rem;
$pickerFieldPadding: (
"top": 1.2rem,
"bottom": 1.2rem,
"left": 1.8rem,
"right": 1.8rem,
) !default;
$pickerFieldPaddingXDense: (
"top": 1.2rem,
"bottom": 1.2rem,
"left": 1rem,
"right": 1rem,
) !default;
$pickerFieldHeightDense: 3rem;
$pickerFieldPaddingDense: (
"top": 0.5rem,
"bottom": 0.5rem,
"left": 0.8rem,
"right": 0.8rem,
) !default;
@mixin pickerStyle($height, $paddings, $fontSize) {
input {
height: $height;
padding-top: map-get($map: $paddings, $key: "top");
padding-bottom: map-get($map: $paddings, $key: "bottom");
padding-left: map-get($map: $paddings, $key: "left");
padding-right: map-get($map: $paddings, $key: "right");
font-size: $fontSize;
}
.rdtPicker {
.rdtCounter {
height: calc(#{$height} - 0.1rem);
left: calc(
(#{map-get($map: $paddings, $key: "left")} - (#{$fontSize} / 1.5)) +
1.2rem
);
&:nth-child(3) {
left: calc(
(#{map-get($map: $paddings, $key: "left")} - (#{$fontSize} / 1.5)) +
3.2rem
);
}
&:nth-child(5) {
left: calc(
(#{map-get($map: $paddings, $key: "left")} - (#{$fontSize} / 1.5)) +
4.7rem
);
}
.rdtBtn {
font-size: calc(#{$fontSize} / 1);
}
}
}
}
.field-group {
position: relative;
&-error {
input {
border-color: red;
color: red !important;
}
}
}
.field-label {
display: flex;
justify-content: center;
margin-bottom: 1.5rem;
color: #172b4d;
font-size: 1.4rem;
font-weight: 600;
line-height: 1;
transform: translate(0) !important;
}
.field-wrap {
width: 7.5rem;
display: flex;
position: relative;
border-radius: 6px;
background-color: #fff;
}
.error {
display: block;
margin-bottom: 8px;
color: red !important;
font-size: 12px;
text-align: right;
}
.kc-timepicker {
input {
width: 100%;
border: none;
border-radius: 0.3125rem;
background-color: transparent;
color: #535a63;
z-index: 1;
}
.rdt {
width: 100%;
> div {
width: 100%;
}
}
.rdtPicker {
min-width: initial;
position: absolute;
top: 0;
left: 0%;
margin: 0;
padding: 0;
opacity: 0;
visibility: hidden;
.rdtCounterSeparator,
.rdtCount {
display: none;
}
.rdtCounter {
width: auto;
display: flex;
flex-direction: column;
justify-content: space-between;
position: absolute;
top: 0;
cursor: pointer;
.rdtBtn {
height: auto;
font-size: 0.625rem;
visibility: visible;
opacity: 0.4;
line-height: 1;
user-select: none;
&:hover {
opacity: 1;
transform: scale(1.5);
background-color: transparent;
}
}
}
}
@include pickerStyle($pickerFieldHeight, $pickerFieldPadding, 1.4rem);
&.outlined {
input {
border: 1px solid $inputBorderColor;
}
}
&.dense {
@include pickerStyle(
$pickerFieldHeightDense,
$pickerFieldPaddingDense,
1.4rem
);
}
&.XDense {
@include pickerStyle($pickerFieldHeight, $pickerFieldPaddingXDense, 1.4rem);
}
&:hover {
.rdtPicker {
opacity: 1;
}
}
}

Step5 — Import CustomTimePicker component in App.js

import React from "react";
import CustomTimePicker from "./custom-time-picker";
import './custom-time-picker.scss';
function App() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center",
        width: "100vw",
        height: "100vh",
      }}
    >
      <CustomTimePicker name="time" label="Time" />
    </div>
  );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

And that’s it!

The post Build a custom time picker React component first appeared on Anlisha Maharjan.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs