feat: create TimeSelector time range component
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
export { TimeSelector } from './time-selector'
|
||||||
|
export type { TimeSelectorProps } from './time-selector'
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
.time-selector {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector__group {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector__label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #666;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector__inputs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector__input {
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.p-inputnumber-input {
|
||||||
|
padding: 8px 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-selector__separator {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { InputNumber } from 'primereact/inputnumber'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import './time-selector.scss'
|
||||||
|
|
||||||
|
export interface TimeSelectorProps {
|
||||||
|
startTime?: string
|
||||||
|
endTime?: string
|
||||||
|
onStartTimeChange?: (time: string) => void
|
||||||
|
onEndTimeChange?: (time: string) => void
|
||||||
|
'data-testid'?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TimeSelector: React.FC<TimeSelectorProps> = ({
|
||||||
|
startTime = '00:00',
|
||||||
|
endTime = '23:59',
|
||||||
|
onStartTimeChange,
|
||||||
|
onEndTimeChange,
|
||||||
|
'data-testid': dataTestId,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const parseTime = (timeStr: string): { hours: number; minutes: number } => {
|
||||||
|
const [h, m] = timeStr.split(':').map(Number)
|
||||||
|
return { hours: h || 0, minutes: m || 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatTime = (hours: number, minutes: number): string => {
|
||||||
|
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStartHoursChange = (value: number | null | undefined) => {
|
||||||
|
if (onStartTimeChange && value !== null && value !== undefined) {
|
||||||
|
const { minutes } = parseTime(startTime)
|
||||||
|
onStartTimeChange(formatTime(value, minutes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStartMinutesChange = (value: number | null | undefined) => {
|
||||||
|
if (onStartTimeChange && value !== null && value !== undefined) {
|
||||||
|
const { hours } = parseTime(startTime)
|
||||||
|
onStartTimeChange(formatTime(hours, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEndHoursChange = (value: number | null | undefined) => {
|
||||||
|
if (onEndTimeChange && value !== null && value !== undefined) {
|
||||||
|
const { minutes } = parseTime(endTime)
|
||||||
|
onEndTimeChange(formatTime(value, minutes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEndMinutesChange = (value: number | null | undefined) => {
|
||||||
|
if (onEndTimeChange && value !== null && value !== undefined) {
|
||||||
|
const { hours } = parseTime(endTime)
|
||||||
|
onEndTimeChange(formatTime(hours, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startParsed = parseTime(startTime)
|
||||||
|
const endParsed = parseTime(endTime)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="time-selector" data-testid={dataTestId || 'time-selector'}>
|
||||||
|
<div className="time-selector__group">
|
||||||
|
<label className="time-selector__label">{t('SHARED.FROM')}</label>
|
||||||
|
<div className="time-selector__inputs">
|
||||||
|
<InputNumber
|
||||||
|
value={startParsed.hours}
|
||||||
|
onValueChange={(e) => handleStartHoursChange(e.value)}
|
||||||
|
min={0}
|
||||||
|
max={23}
|
||||||
|
className="time-selector__input"
|
||||||
|
data-testid="time-selector-start-hours"
|
||||||
|
/>
|
||||||
|
<span className="time-selector__separator">:</span>
|
||||||
|
<InputNumber
|
||||||
|
value={startParsed.minutes}
|
||||||
|
onValueChange={(e) => handleStartMinutesChange(e.value)}
|
||||||
|
min={0}
|
||||||
|
max={59}
|
||||||
|
className="time-selector__input"
|
||||||
|
data-testid="time-selector-start-minutes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="time-selector__group">
|
||||||
|
<label className="time-selector__label">{t('SHARED.TO')}</label>
|
||||||
|
<div className="time-selector__inputs">
|
||||||
|
<InputNumber
|
||||||
|
value={endParsed.hours}
|
||||||
|
onValueChange={(e) => handleEndHoursChange(e.value)}
|
||||||
|
min={0}
|
||||||
|
max={23}
|
||||||
|
className="time-selector__input"
|
||||||
|
data-testid="time-selector-end-hours"
|
||||||
|
/>
|
||||||
|
<span className="time-selector__separator">:</span>
|
||||||
|
<InputNumber
|
||||||
|
value={endParsed.minutes}
|
||||||
|
onValueChange={(e) => handleEndMinutesChange(e.value)}
|
||||||
|
min={0}
|
||||||
|
max={59}
|
||||||
|
className="time-selector__input"
|
||||||
|
data-testid="time-selector-end-minutes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user