add support for 24hr time to TimePicker

This commit is contained in:
Gimle Larpes 2025-07-25 01:05:53 +02:00
parent 78ac14eb6b
commit 77b551d004
3 changed files with 76 additions and 50 deletions

View file

@ -4,6 +4,8 @@ import dayjs from 'dayjs';
import * as css from './styles.css'; import * as css from './styles.css';
import { PickerColumn } from './PickerColumn'; import { PickerColumn } from './PickerColumn';
import { hour12to24, hour24to12, hoursToMs, inSameDay, minutesToMs } from '../../utils/time'; import { hour12to24, hour24to12, hoursToMs, inSameDay, minutesToMs } from '../../utils/time';
import { useSetting } from '../../state/hooks/settings';
import { settingsAtom } from '../../state/settings';
type TimePickerProps = { type TimePickerProps = {
min: number; min: number;
@ -13,9 +15,11 @@ type TimePickerProps = {
}; };
export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>( export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(
({ min, max, value, onChange }, ref) => { ({ min, max, value, onChange }, ref) => {
const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
const hour24 = dayjs(value).hour(); const hour24 = dayjs(value).hour();
const selectedHour = hour24to12(hour24); const selectedHour = hour24Clock ? hour24 : hour24to12(hour24);
const selectedMinute = dayjs(value).minute(); const selectedMinute = dayjs(value).minute();
const selectedPM = hour24 >= 12; const selectedPM = hour24 >= 12;
@ -24,7 +28,7 @@ export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(
}; };
const handleHour = (hour: number) => { const handleHour = (hour: number) => {
const seconds = hoursToMs(hour12to24(hour, selectedPM)); const seconds = hoursToMs(hour24Clock ? hour : hour12to24(hour, selectedPM));
const lastSeconds = hoursToMs(hour24); const lastSeconds = hoursToMs(hour24);
const newValue = value + (seconds - lastSeconds); const newValue = value + (seconds - lastSeconds);
handleSubmit(newValue); handleSubmit(newValue);
@ -59,28 +63,43 @@ export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(
<Menu className={css.PickerMenu} ref={ref}> <Menu className={css.PickerMenu} ref={ref}>
<Box direction="Row" gap="200" className={css.PickerContainer}> <Box direction="Row" gap="200" className={css.PickerContainer}>
<PickerColumn title="Hour"> <PickerColumn title="Hour">
{Array.from(Array(12).keys()) {hour24Clock
.map((i) => { ? Array.from(Array(24).keys()).map((hour) => (
if (i === 0) return 12; <Chip
return i; key={hour}
}) size="500"
.map((hour) => ( variant={hour === selectedHour ? 'Primary' : 'Background'}
<Chip fill="None"
key={hour} radii="300"
size="500" aria-selected={hour === selectedHour}
variant={hour === selectedHour ? 'Primary' : 'Background'} onClick={() => handleHour(hour)}
fill="None" disabled={(minDay && hour < minHour24) || (maxDay && hour > maxHour24)}
radii="300" >
aria-selected={hour === selectedHour} <Text size="T300">{hour < 10 ? `0${hour}` : hour}</Text>
onClick={() => handleHour(hour)} </Chip>
disabled={ ))
(minDay && hour12to24(hour, selectedPM) < minHour24) || : Array.from(Array(12).keys())
(maxDay && hour12to24(hour, selectedPM) > maxHour24) .map((i) => {
} if (i === 0) return 12;
> return i;
<Text size="T300">{hour < 10 ? `0${hour}` : hour}</Text> })
</Chip> .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>
<PickerColumn title="Minutes"> <PickerColumn title="Minutes">
{Array.from(Array(60).keys()).map((minute) => ( {Array.from(Array(60).keys()).map((minute) => (
@ -101,30 +120,32 @@ export const TimePicker = forwardRef<HTMLDivElement, TimePickerProps>(
</Chip> </Chip>
))} ))}
</PickerColumn> </PickerColumn>
<PickerColumn title="Period"> {!hour24Clock && (
<Chip <PickerColumn title="Period">
size="500" <Chip
variant={!selectedPM ? 'Primary' : 'SurfaceVariant'} size="500"
fill="None" variant={!selectedPM ? 'Primary' : 'SurfaceVariant'}
radii="300" fill="None"
aria-selected={!selectedPM} radii="300"
onClick={() => handlePeriod(false)} aria-selected={!selectedPM}
disabled={minDay && minPM} onClick={() => handlePeriod(false)}
> disabled={minDay && minPM}
<Text size="T300">AM</Text> >
</Chip> <Text size="T300">AM</Text>
<Chip </Chip>
size="500" <Chip
variant={selectedPM ? 'Primary' : 'SurfaceVariant'} size="500"
fill="None" variant={selectedPM ? 'Primary' : 'SurfaceVariant'}
radii="300" fill="None"
aria-selected={selectedPM} radii="300"
onClick={() => handlePeriod(true)} aria-selected={selectedPM}
disabled={maxDay && !maxPM} onClick={() => handlePeriod(true)}
> disabled={maxDay && !maxPM}
<Text size="T300">PM</Text> >
</Chip> <Text size="T300">PM</Text>
</PickerColumn> </Chip>
</PickerColumn>
)}
</Box> </Box>
</Menu> </Menu>
); );

View file

@ -29,6 +29,8 @@ import { useRoom } from '../../../hooks/useRoom';
import { StateEvent } from '../../../../types/matrix/room'; import { StateEvent } from '../../../../types/matrix/room';
import { getToday, getYesterday, timeDayMonthYear, timeHourMinute } from '../../../utils/time'; import { getToday, getYesterday, timeDayMonthYear, timeHourMinute } from '../../../utils/time';
import { DatePicker, TimePicker } from '../../../components/time-date'; import { DatePicker, TimePicker } from '../../../components/time-date';
import { useSetting } from '../../../state/hooks/settings';
import { settingsAtom } from '../../../state/settings';
type JumpToTimeProps = { type JumpToTimeProps = {
onCancel: () => void; onCancel: () => void;
@ -45,6 +47,8 @@ export function JumpToTime({ onCancel, onSubmit }: JumpToTimeProps) {
const createTs = useMemo(() => createStateEvent?.getTs() ?? 0, [createStateEvent]); const createTs = useMemo(() => createStateEvent?.getTs() ?? 0, [createStateEvent]);
const [ts, setTs] = useState(() => Date.now()); const [ts, setTs] = useState(() => Date.now());
const [hour24Clock] = useSetting(settingsAtom, 'hour24Clock');
const [timePickerCords, setTimePickerCords] = useState<RectCords>(); const [timePickerCords, setTimePickerCords] = useState<RectCords>();
const [datePickerCords, setDatePickerCords] = useState<RectCords>(); const [datePickerCords, setDatePickerCords] = useState<RectCords>();
@ -125,7 +129,7 @@ export function JumpToTime({ onCancel, onSubmit }: JumpToTimeProps) {
after={<Icon size="50" src={Icons.ChevronBottom} />} after={<Icon size="50" src={Icons.ChevronBottom} />}
onClick={handleTimePicker} onClick={handleTimePicker}
> >
<Text size="B300">{timeHourMinute(ts)}</Text> <Text size="B300">{timeHourMinute(ts, hour24Clock)}</Text>
</Chip> </Chip>
<PopOut <PopOut
anchor={timePickerCords} anchor={timePickerCords}

View file

@ -9,7 +9,8 @@ export const today = (ts: number): boolean => dayjs(ts).isToday();
export const yesterday = (ts: number): boolean => dayjs(ts).isYesterday(); export const yesterday = (ts: number): boolean => dayjs(ts).isYesterday();
export const timeHour = (ts: number): string => dayjs(ts).format('hh'); export const timeHour = (ts: number, hour24Clock: boolean): string =>
dayjs(ts).format(hour24Clock ? 'HH' : 'hh');
export const timeMinute = (ts: number): string => dayjs(ts).format('mm'); export const timeMinute = (ts: number): string => dayjs(ts).format('mm');
export const timeAmPm = (ts: number): string => dayjs(ts).format('A'); export const timeAmPm = (ts: number): string => dayjs(ts).format('A');
export const timeDay = (ts: number): string => dayjs(ts).format('D'); export const timeDay = (ts: number): string => dayjs(ts).format('D');