mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-09-16 15:52:25 +03:00
add time and date picker components
This commit is contained in:
parent
3ed8260877
commit
3263dbd6da
5 changed files with 302 additions and 0 deletions
132
src/app/components/time-date/TimePicker.tsx
Normal file
132
src/app/components/time-date/TimePicker.tsx
Normal file
|
@ -0,0 +1,132 @@
|
|||
import React, { forwardRef } from 'react';
|
||||
import { Menu, Box, Text, Chip } from 'folds';
|
||||
import dayjs from 'dayjs';
|
||||
import * as css from './styles.css';
|
||||
import { PickerColumn } from './PickerColumn';
|
||||
import { hour12to24, hour24to12, hoursToMs, inSameDay, minutesToMs } from '../../utils/time';
|
||||
|
||||
type TimePickerProps = {
|
||||
min: number;
|
||||
max: number;
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
};
|
||||
export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(
|
||||
({ min, max, value, onChange }, ref) => {
|
||||
const hour24 = dayjs(value).hour();
|
||||
|
||||
const selectedHour = hour24to12(hour24);
|
||||
const selectedMinute = dayjs(value).minute();
|
||||
const selectedPM = hour24 >= 12;
|
||||
|
||||
const handleSubmit = (newValue: number) => {
|
||||
onChange(Math.min(Math.max(min, newValue), max));
|
||||
};
|
||||
|
||||
const handleHour = (hour: number) => {
|
||||
const seconds = hoursToMs(hour12to24(hour, selectedPM));
|
||||
const lastSeconds = hoursToMs(hour24);
|
||||
const newValue = value + (seconds - lastSeconds);
|
||||
handleSubmit(newValue);
|
||||
};
|
||||
|
||||
const handleMinute = (minute: number) => {
|
||||
const seconds = minutesToMs(minute);
|
||||
const lastSeconds = minutesToMs(selectedMinute);
|
||||
const newValue = value + (seconds - lastSeconds);
|
||||
handleSubmit(newValue);
|
||||
};
|
||||
|
||||
const handlePeriod = (pm: boolean) => {
|
||||
const seconds = hoursToMs(hour12to24(selectedHour, pm));
|
||||
const lastSeconds = hoursToMs(hour24);
|
||||
const newValue = value + (seconds - lastSeconds);
|
||||
handleSubmit(newValue);
|
||||
};
|
||||
|
||||
const minHour24 = dayjs(min).hour();
|
||||
const maxHour24 = dayjs(max).hour();
|
||||
|
||||
const minMinute = dayjs(min).minute();
|
||||
const maxMinute = dayjs(max).minute();
|
||||
const minPM = minHour24 >= 12;
|
||||
const maxPM = maxHour24 >= 12;
|
||||
|
||||
const minDay = inSameDay(min, value);
|
||||
const maxDay = inSameDay(max, value);
|
||||
|
||||
return (
|
||||
<Menu className={css.PickerMenu} ref={ref}>
|
||||
<Box direction="Row" gap="200" className={css.PickerContainer}>
|
||||
<PickerColumn title="Hour">
|
||||
{Array.from(Array(12).keys())
|
||||
.map((i) => {
|
||||
if (i === 0) return 12;
|
||||
return i;
|
||||
})
|
||||
.map((hour) => (
|
||||
<Chip
|
||||
key={hour}
|
||||
size="500"
|
||||
variant={hour === selectedHour ? 'Primary' : 'Background'}
|
||||
fill="None"
|
||||
radii="300"
|
||||
aria-selected={hour === selectedHour}
|
||||
onClick={() => handleHour(hour)}
|
||||
disabled={
|
||||
(minDay && hour12to24(hour, selectedPM) < minHour24) ||
|
||||
(maxDay && hour12to24(hour, selectedPM) > maxHour24)
|
||||
}
|
||||
>
|
||||
<Text size="T300">{hour < 10 ? `0${hour}` : hour}</Text>
|
||||
</Chip>
|
||||
))}
|
||||
</PickerColumn>
|
||||
<PickerColumn title="Minutes">
|
||||
{Array.from(Array(60).keys()).map((minute) => (
|
||||
<Chip
|
||||
key={minute}
|
||||
size="500"
|
||||
variant={minute === selectedMinute ? 'Primary' : 'Background'}
|
||||
fill="None"
|
||||
radii="300"
|
||||
aria-selected={minute === selectedMinute}
|
||||
onClick={() => handleMinute(minute)}
|
||||
disabled={
|
||||
(minDay && hour24 === minHour24 && minute < minMinute) ||
|
||||
(maxDay && hour24 === maxHour24 && minute > maxMinute)
|
||||
}
|
||||
>
|
||||
<Text size="T300">{minute < 10 ? `0${minute}` : minute}</Text>
|
||||
</Chip>
|
||||
))}
|
||||
</PickerColumn>
|
||||
<PickerColumn title="Period">
|
||||
<Chip
|
||||
size="500"
|
||||
variant={!selectedPM ? 'Primary' : 'SurfaceVariant'}
|
||||
fill="None"
|
||||
radii="300"
|
||||
aria-selected={!selectedPM}
|
||||
onClick={() => handlePeriod(false)}
|
||||
disabled={minDay && minPM}
|
||||
>
|
||||
<Text size="T300">AM</Text>
|
||||
</Chip>
|
||||
<Chip
|
||||
size="500"
|
||||
variant={selectedPM ? 'Primary' : 'SurfaceVariant'}
|
||||
fill="None"
|
||||
radii="300"
|
||||
aria-selected={selectedPM}
|
||||
onClick={() => handlePeriod(true)}
|
||||
disabled={maxDay && !maxPM}
|
||||
>
|
||||
<Text size="T300">PM</Text>
|
||||
</Chip>
|
||||
</PickerColumn>
|
||||
</Box>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue