Skip to content

Commit

Permalink
Add minTime and maxTime support to TimeInput.tsx
Browse files Browse the repository at this point in the history
  • Loading branch information
snturk committed Feb 26, 2024
1 parent 08e581e commit 2c97436
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,20 @@ export function WithSeconds() {
}

export function MinDate() {
const minDate = new Date();
minDate.setHours(0, 30, 0, 0);

const maxDate = new Date();
maxDate.setDate(maxDate.getDate() + 5);
maxDate.setHours(22, 30, 0, 0);
return (
<div style={{ padding: 40, maxWidth: 400 }}>
<DateTimePicker placeholder="Date time picker" withSeconds minDate={new Date()} />
<DateTimePicker
placeholder="Date time picker"
withSeconds
minDate={minDate}
maxDate={maxDate}
/>
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export const DateTimePicker = factory<DateTimePickerFactory>((_props, ref) => {
variant,
dropdownType,
vars,
minDate,
maxDate,
...rest
} = props;

Expand Down Expand Up @@ -190,6 +192,9 @@ export const DateTimePicker = factory<DateTimePickerFactory>((_props, ref) => {
}
}, [dropdownOpened]);

const minTime = minDate ? dayjs(minDate).format('HH:mm:ss') : null;
const maxTime = maxDate ? dayjs(maxDate).format('HH:mm:ss') : null;

const __stopPropagation = dropdownType === 'popover';

return (
Expand All @@ -213,6 +218,8 @@ export const DateTimePicker = factory<DateTimePickerFactory>((_props, ref) => {
>
<DatePicker
{...calendarProps}
maxDate={maxDate}
minDate={minDate}
size={size}
variant={variant}
type="default"
Expand Down Expand Up @@ -248,6 +255,20 @@ export const DateTimePicker = factory<DateTimePickerFactory>((_props, ref) => {
})}
onChange={handleTimeChange}
onKeyDown={handleTimeInputKeyDown}
minTime={
_value && minDate && _value.toDateString() === minDate.toDateString()
? minTime != null
? minTime
: undefined
: undefined
}
maxTime={
_value && maxDate && _value.toDateString() === maxDate.toDateString()
? maxTime != null
? maxTime
: undefined
: undefined
}
size={size}
data-mantine-stop-propagation={__stopPropagation || undefined}
/>
Expand Down
64 changes: 63 additions & 1 deletion packages/@mantine/dates/src/components/TimeInput/TimeInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export interface TimeInputProps
ElementProps<'input', 'size'> {
/** Determines whether seconds input should be rendered */
withSeconds?: boolean;

/** Minimum possible string time, if withSeconds is true, time should be in format HH:mm:ss, otherwise HH:mm */
minTime?: string;

/** Maximum possible string time, if withSeconds is true, time should be in format HH:mm:ss, otherwise HH:mm */
maxTime?: string;
}

export type TimeInputFactory = Factory<{
Expand All @@ -33,14 +39,69 @@ const defaultProps: Partial<TimeInputProps> = {};

export const TimeInput = factory<TimeInputFactory>((_props, ref) => {
const props = useProps('TimeInput', defaultProps, _props);
const { classNames, styles, unstyled, vars, withSeconds, ...others } = props;
const {
classNames,
styles,
unstyled,
vars,
withSeconds,
minTime,
maxTime,
value,
onChange,
...others
} = props;

const { resolvedClassNames, resolvedStyles } = useResolvedStylesApi<TimeInputFactory>({
classNames,
styles,
props,
});

const onTimeChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (minTime !== undefined && maxTime !== undefined) {
const val = event.currentTarget.value;

if (val) {
const [hours, minutes, seconds] = val.split(':').map(Number);
const isValid =
(hours.toString().length === 2 || hours === 0) &&
(minutes.toString().length === 2 || minutes === 0) &&
(withSeconds ? seconds.toString().length === 2 || seconds === 0 : true);

if (isValid) {
if (minTime) {
const [minHours, minMinutes, minSeconds] = minTime.split(':').map(Number);

if (
hours < minHours ||
(hours === minHours && minutes < minMinutes) ||
(withSeconds && hours === minHours && minutes === minMinutes && seconds < minSeconds)
) {
event.currentTarget.value = minTime;
}
}

if (maxTime) {
const [maxHours, maxMinutes, maxSeconds] = maxTime.split(':').map(Number);

if (
hours > maxHours ||
(hours === maxHours && minutes > maxMinutes) ||
(withSeconds && hours === maxHours && minutes === maxMinutes && seconds > maxSeconds)
) {
event.currentTarget.value = maxTime;
}
}
}
}
}

if (onChange) {
onChange(event);
}
};

return (
<InputBase
classNames={{ ...resolvedClassNames, input: cx(classes.input, resolvedClassNames?.input) }}
Expand All @@ -49,6 +110,7 @@ export const TimeInput = factory<TimeInputFactory>((_props, ref) => {
ref={ref}
{...others}
step={withSeconds ? 1 : 60}
onChange={onTimeChange}
type="time"
__staticSelector="TimeInput"
/>
Expand Down

0 comments on commit 2c97436

Please sign in to comment.