| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- import dayjs from 'dayjs'
- import { isNaN, noop } from 'lodash'
- import { Clock } from 'lucide-react'
- import { ChangeEvent, useEffect, useState } from 'react'
- import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
- import type { Time } from './PITR.types'
- import { formatNumberToTwoDigits, formatTimeToTimeString } from './PITR.utils'
- // [Joshen] This is trying to do the same thing as TimeSplitInput.tsx
- // so can we please look to try to combine these 2 components together if possible
- // The problem with TimeSplitInput is that it's tightly coupled to the date + its
- // tightly coupled to the context of a range. Ideally it should be more modular
- // which is what this component is trying to achieve
- // [Joshen] Potential extension, give option to toggle 24 hours or AM/PM
- interface TimeInputProps {
- defaultTime?: Time
- minimumTime?: Time
- maximumTime?: Time
- onChange?: (time: Time) => void
- }
- const TimeInput = ({ defaultTime, minimumTime, maximumTime, onChange = noop }: TimeInputProps) => {
- const [isFocused, setIsFocused] = useState(false)
- const [error, setError] = useState<string>()
- const [time, setTime] = useState<Time>(defaultTime || { h: 0, m: 0, s: 0 })
- const formattedMinimumTime = minimumTime
- ? dayjs(formatTimeToTimeString(minimumTime), 'HH:mm:ss', true)
- : undefined
- const formattedMaximumTime = maximumTime
- ? dayjs(formatTimeToTimeString(maximumTime), 'HH:mm:ss', true)
- : undefined
- useEffect(() => {
- if (minimumTime || maximumTime) validate(time)
- }, [JSON.stringify(minimumTime), JSON.stringify(maximumTime)])
- useEffect(() => {
- if (defaultTime) {
- setTime(defaultTime)
- validate(defaultTime)
- }
- }, [defaultTime])
- const validate = (time: Time) => {
- let error = undefined
- const formattedTime = dayjs(formatTimeToTimeString(time), 'HH:mm:ss', true)
- if (!formattedTime.isValid()) {
- error = 'Please enter a valid time'
- } else if (formattedMinimumTime && formattedTime.isBefore(formattedMinimumTime)) {
- error = 'Selected time is before the minimum time allowed'
- } else if (formattedMaximumTime && formattedTime.isAfter(formattedMaximumTime)) {
- error = 'Selected time is after the maximum time allowed'
- }
- setError(error)
- return error === undefined
- }
- const onFocus = () => setIsFocused(true)
- const onInputChange = (event: ChangeEvent<HTMLInputElement>, unit: 'h' | 'm' | 's') => {
- if (isNaN(Number(event.target.value))) return
- setTime({ ...time, [unit]: event.target.value })
- }
- const onInputBlur = (event: ChangeEvent<HTMLInputElement>, unit: 'h' | 'm' | 's') => {
- const formattedInput = Number(event.target.value)
- const updatedTime = { ...time, [unit]: formattedInput }
- setTime(updatedTime)
- validate(updatedTime)
- onChange(updatedTime)
- setIsFocused(false)
- }
- return (
- <>
- <div
- className={[
- 'flex items-center justify-between transition',
- 'rounded-md bg-studio border px-3.5 py-2 w-[200px]',
- `${
- isFocused ? 'border-stronger' : error === undefined ? 'border-strong' : 'border-red-800'
- }`,
- ].join(' ')}
- >
- <Clock className="text-foreground-light" size={18} strokeWidth={1.5} />
- <Tooltip>
- <TooltipTrigger className="w-1/4" tabIndex={-1}>
- <input
- type="text"
- maxLength={2}
- pattern="[0-9]*"
- placeholder="HH"
- value={formatNumberToTwoDigits(time.h)}
- onFocus={onFocus}
- aria-label="Hours"
- onBlur={(event) => onInputBlur(event, 'h')}
- onChange={(event) => onInputChange(event, 'h')}
- className="w-full text-sm bg-transparent p-0 text-center outline-hidden border-none focus:ring-0"
- />
- </TooltipTrigger>
- <TooltipContent side="bottom">Hours (HH)</TooltipContent>
- </Tooltip>
- <span>:</span>
- <Tooltip>
- <TooltipTrigger className="w-1/4" tabIndex={-1}>
- <input
- type="text"
- maxLength={2}
- pattern="[0-9]*"
- placeholder="MM"
- value={formatNumberToTwoDigits(time.m)}
- onFocus={onFocus}
- aria-label="Minutes"
- onBlur={(event) => onInputBlur(event, 'm')}
- onChange={(event) => onInputChange(event, 'm')}
- className="w-full text-sm bg-transparent p-0 text-center outline-hidden border-none focus:ring-0"
- />
- </TooltipTrigger>
- <TooltipContent side="bottom">Minutes (MM)</TooltipContent>
- </Tooltip>
- <span>:</span>
- <Tooltip>
- <TooltipTrigger className="w-1/4" tabIndex={-1}>
- <input
- type="text"
- maxLength={2}
- pattern="[0-9]*"
- placeholder="SS"
- value={formatNumberToTwoDigits(time.s)}
- onFocus={onFocus}
- aria-label="Seconds"
- onBlur={(event) => onInputBlur(event, 's')}
- onChange={(event) => onInputChange(event, 's')}
- className="w-full text-sm bg-transparent p-0 text-center outline-hidden border-none focus:ring-0"
- />
- </TooltipTrigger>
- <TooltipContent side="bottom">Seconds (SS)</TooltipContent>
- </Tooltip>
- </div>
- {error && <p className="text-sm text-red-900">{error}</p>}
- </>
- )
- }
- export default TimeInput
|