ui: fix timepicker and timezone interactions
This commit is contained in:
parent
7d6f838d25
commit
ee71625499
1 changed files with 100 additions and 37 deletions
|
|
@ -12,60 +12,123 @@ import {
|
||||||
getDateRangeFromValue,
|
getDateRangeFromValue,
|
||||||
getDateRangeLabel,
|
getDateRangeLabel,
|
||||||
} from 'App/dateRange';
|
} from 'App/dateRange';
|
||||||
import { DateTime, Interval } from 'luxon';
|
import { DateTime, Interval, Settings } from 'luxon';
|
||||||
|
|
||||||
import styles from './dateRangePopup.module.css';
|
import styles from './dateRangePopup.module.css';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
function DateRangePopup(props: any) {
|
function DateRangePopup(props: any) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [displayDates, setDisplayDates] = React.useState<[Date, Date]>([new Date(), new Date()]);
|
||||||
const [range, setRange] = React.useState(
|
const [range, setRange] = React.useState(
|
||||||
props.selectedDateRange ||
|
props.selectedDateRange ||
|
||||||
Interval.fromDateTimes(DateTime.now(), DateTime.now()),
|
Interval.fromDateTimes(DateTime.now(), DateTime.now()),
|
||||||
);
|
);
|
||||||
const [value, setValue] = React.useState<string | null>(null);
|
const [value, setValue] = React.useState<string | null>(null);
|
||||||
|
|
||||||
const selectCustomRange = (range) => {
|
React.useEffect(() => {
|
||||||
let newRange;
|
if (props.selectedDateRange) {
|
||||||
if (props.singleDay) {
|
const start = new Date(
|
||||||
newRange = Interval.fromDateTimes(
|
props.selectedDateRange.start.year,
|
||||||
DateTime.fromJSDate(range),
|
props.selectedDateRange.start.month - 1, // JS months are 0-based
|
||||||
DateTime.fromJSDate(range),
|
props.selectedDateRange.start.day
|
||||||
);
|
);
|
||||||
} else {
|
const end = new Date(
|
||||||
newRange = Interval.fromDateTimes(
|
props.selectedDateRange.end.year,
|
||||||
DateTime.fromJSDate(range[0]),
|
props.selectedDateRange.end.month - 1,
|
||||||
DateTime.fromJSDate(range[1]),
|
props.selectedDateRange.end.day
|
||||||
);
|
);
|
||||||
}
|
setDisplayDates([start, end]);
|
||||||
setRange(newRange);
|
}
|
||||||
|
}, [props.selectedDateRange]);
|
||||||
|
|
||||||
|
const createNaiveTime = (dateTime: DateTime) => {
|
||||||
|
if (!dateTime) return null;
|
||||||
|
return DateTime.fromObject({
|
||||||
|
hour: dateTime.hour,
|
||||||
|
minute: dateTime.minute
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const selectCustomRange = (newDates: [Date, Date]) => {
|
||||||
|
if (!newDates || !newDates[0] || !newDates[1]) return;
|
||||||
|
|
||||||
|
setDisplayDates(newDates);
|
||||||
|
|
||||||
|
const selectedTzStart = DateTime.fromObject({
|
||||||
|
year: newDates[0].getFullYear(),
|
||||||
|
month: newDates[0].getMonth() + 1,
|
||||||
|
day: newDates[0].getDate(),
|
||||||
|
hour: 0,
|
||||||
|
minute: 0
|
||||||
|
}).setZone(Settings.defaultZone);
|
||||||
|
|
||||||
|
const selectedTzEnd = DateTime.fromObject({
|
||||||
|
year: newDates[1].getFullYear(),
|
||||||
|
month: newDates[1].getMonth() + 1,
|
||||||
|
day: newDates[1].getDate(),
|
||||||
|
hour: 23,
|
||||||
|
minute: 59
|
||||||
|
}).setZone(Settings.defaultZone);
|
||||||
|
|
||||||
|
const updatedRange = Interval.fromDateTimes(selectedTzStart, selectedTzEnd);
|
||||||
|
setRange(updatedRange);
|
||||||
setValue(CUSTOM_RANGE);
|
setValue(CUSTOM_RANGE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setRangeTimeStart = (value: DateTime) => {
|
const setRangeTimeStart = (naiveTime: DateTime) => {
|
||||||
if (!range.end || value > range.end) {
|
if (!range.end || !naiveTime) return;
|
||||||
return;
|
|
||||||
}
|
const newStart = range.start.set({
|
||||||
const newRange = range.start.set({
|
hour: naiveTime.hour,
|
||||||
hour: value.hour,
|
minute: naiveTime.minute
|
||||||
minute: value.minute,
|
|
||||||
});
|
});
|
||||||
setRange(Interval.fromDateTimes(newRange, range.end));
|
|
||||||
|
if (newStart > range.end) return;
|
||||||
|
|
||||||
|
setRange(Interval.fromDateTimes(newStart, range.end));
|
||||||
setValue(CUSTOM_RANGE);
|
setValue(CUSTOM_RANGE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setRangeTimeEnd = (value: DateTime) => {
|
const setRangeTimeEnd = (naiveTime: DateTime) => {
|
||||||
if (!range.start || (value && value < range.start)) {
|
if (!range.start || !naiveTime) return;
|
||||||
return;
|
|
||||||
}
|
const newEnd = range.end.set({
|
||||||
const newRange = range.end.set({ hour: value.hour, minute: value.minute });
|
hour: naiveTime.hour,
|
||||||
setRange(Interval.fromDateTimes(range.start, newRange));
|
minute: naiveTime.minute
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newEnd < range.start) return;
|
||||||
|
|
||||||
|
setRange(Interval.fromDateTimes(range.start, newEnd));
|
||||||
setValue(CUSTOM_RANGE);
|
setValue(CUSTOM_RANGE);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectValue = (value: string) => {
|
const selectValue = (value: string) => {
|
||||||
const range = getDateRangeFromValue(value);
|
const newRange = getDateRangeFromValue(value);
|
||||||
setRange(range);
|
|
||||||
|
if (!newRange.start || !newRange.end) {
|
||||||
|
setRange(Interval.fromDateTimes(DateTime.now(), DateTime.now()));
|
||||||
|
setDisplayDates([new Date(), new Date()]);
|
||||||
|
setValue(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const zonedStart = newRange.start.setZone(Settings.defaultZone);
|
||||||
|
const zonedEnd = newRange.end.setZone(Settings.defaultZone);
|
||||||
|
setRange(Interval.fromDateTimes(zonedStart, zonedEnd));
|
||||||
|
|
||||||
|
const start = new Date(
|
||||||
|
zonedStart.year,
|
||||||
|
zonedStart.month - 1,
|
||||||
|
zonedStart.day
|
||||||
|
);
|
||||||
|
const end = new Date(
|
||||||
|
zonedEnd.year,
|
||||||
|
zonedEnd.month - 1,
|
||||||
|
zonedEnd.day
|
||||||
|
);
|
||||||
|
setDisplayDates([start, end]);
|
||||||
setValue(value);
|
setValue(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -77,9 +140,9 @@ function DateRangePopup(props: any) {
|
||||||
const isUSLocale =
|
const isUSLocale =
|
||||||
navigator.language === 'en-US' || navigator.language.startsWith('en-US');
|
navigator.language === 'en-US' || navigator.language.startsWith('en-US');
|
||||||
|
|
||||||
const rangeForDisplay = props.singleDay
|
const naiveStartTime = createNaiveTime(range.start);
|
||||||
? range.start.ts
|
const naiveEndTime = createNaiveTime(range.end);
|
||||||
: [range.start!.startOf('day').ts, range.end!.startOf('day').ts];
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<div className={`${styles.body} h-fit`}>
|
<div className={`${styles.body} h-fit`}>
|
||||||
|
|
@ -103,7 +166,7 @@ function DateRangePopup(props: any) {
|
||||||
shouldCloseCalendar={() => false}
|
shouldCloseCalendar={() => false}
|
||||||
isOpen
|
isOpen
|
||||||
maxDate={new Date()}
|
maxDate={new Date()}
|
||||||
value={rangeForDisplay}
|
value={displayDates}
|
||||||
calendarProps={{
|
calendarProps={{
|
||||||
tileDisabled: props.isTileDisabled,
|
tileDisabled: props.isTileDisabled,
|
||||||
selectRange: !props.singleDay,
|
selectRange: !props.singleDay,
|
||||||
|
|
@ -122,7 +185,7 @@ function DateRangePopup(props: any) {
|
||||||
<span>{range.start.toFormat(isUSLocale ? 'MM/dd' : 'dd/MM')} </span>
|
<span>{range.start.toFormat(isUSLocale ? 'MM/dd' : 'dd/MM')} </span>
|
||||||
<TimePicker
|
<TimePicker
|
||||||
format={isUSLocale ? 'hh:mm a' : 'HH:mm'}
|
format={isUSLocale ? 'hh:mm a' : 'HH:mm'}
|
||||||
value={range.start}
|
value={naiveStartTime}
|
||||||
onChange={setRangeTimeStart}
|
onChange={setRangeTimeStart}
|
||||||
needConfirm={false}
|
needConfirm={false}
|
||||||
showNow={false}
|
showNow={false}
|
||||||
|
|
@ -132,7 +195,7 @@ function DateRangePopup(props: any) {
|
||||||
<span>{range.end.toFormat(isUSLocale ? 'MM/dd' : 'dd/MM')} </span>
|
<span>{range.end.toFormat(isUSLocale ? 'MM/dd' : 'dd/MM')} </span>
|
||||||
<TimePicker
|
<TimePicker
|
||||||
format={isUSLocale ? 'hh:mm a' : 'HH:mm'}
|
format={isUSLocale ? 'hh:mm a' : 'HH:mm'}
|
||||||
value={range.end}
|
value={naiveEndTime}
|
||||||
onChange={setRangeTimeEnd}
|
onChange={setRangeTimeEnd}
|
||||||
needConfirm={false}
|
needConfirm={false}
|
||||||
showNow={false}
|
showNow={false}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue