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
Step2 — Install react-datetime and react-input-mask packages
cd axon
npm i react-datetime react-input-mask moment
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;
And that’s it!
The post Build a custom time picker React component first appeared on Anlisha Maharjan.
Top comments (0)