@grafana/ui
Version:
Grafana Components Library
204 lines (202 loc) • 7.44 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { cx, css } from '@emotion/css';
import '@floating-ui/react';
import '@react-aria/dialog';
import '@react-aria/focus';
import '@react-aria/overlays';
import * as React from 'react';
import { useState, useEffect, useCallback } from 'react';
import Calendar from 'react-calendar';
import 'react-use';
import { dateTimeFormat, dateTime, isDateTime, dateTimeForTimeZone, getTimeZone } from '@grafana/data';
import { Components } from '@grafana/e2e-selectors';
import { useStyles2 } from '../../../themes/ThemeContext.mjs';
import 'micro-memoize';
import '@emotion/react';
import 'tinycolor2';
import '../../../utils/skeleton.mjs';
import { t, Trans } from '../../../utils/i18n.mjs';
import { Button } from '../../Button/Button.mjs';
import { InlineField } from '../../Forms/InlineField.mjs';
import { Icon } from '../../Icon/Icon.mjs';
import { Input } from '../../Input/Input.mjs';
import { Stack } from '../../Layout/Stack/Stack.mjs';
import '../../Portal/Portal.mjs';
import { TimeOfDayPicker } from '../TimeOfDayPicker.mjs';
import { getBodyStyles } from '../TimeRangePicker/CalendarBody.mjs';
import { isValid } from '../utils.mjs';
import { adjustDateForReactCalendar } from '../utils/adjustDateForReactCalendar.mjs';
const DateTimeInput = React.forwardRef(
({ date, label, onChange, onOpen, timeZone, showSeconds = true, clearable = false }, ref) => {
const styles = useStyles2(getStyles);
const format = showSeconds ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD HH:mm";
const [internalDate, setInternalDate] = useState(() => {
return {
value: date ? dateTimeFormat(date, { timeZone }) : !clearable ? dateTimeFormat(dateTime(), { timeZone }) : "",
invalid: false
};
});
useEffect(() => {
if (date) {
const formattedDate = dateTimeFormat(date, { format, timeZone });
setInternalDate({
invalid: !isValid(formattedDate),
value: isDateTime(date) ? formattedDate : date
});
}
}, [date, format, timeZone]);
const onChangeDate = useCallback((event) => {
const isInvalid = !isValid(event.currentTarget.value);
setInternalDate({
value: event.currentTarget.value,
invalid: isInvalid
});
}, []);
const onBlur = useCallback(() => {
if (!internalDate.invalid && internalDate.value) {
const date2 = dateTimeForTimeZone(getTimeZone({ timeZone }), internalDate.value);
onChange(date2);
}
}, [internalDate, onChange, timeZone]);
const clearInternalDate = useCallback(() => {
setInternalDate({ value: "", invalid: false });
onChange();
}, [onChange]);
const icon = /* @__PURE__ */ jsx(
Button,
{
"aria-label": t("grafana-ui.date-time-picker.calendar-icon-label", "Time picker"),
icon: "calendar-alt",
variant: "secondary",
onClick: onOpen
}
);
return /* @__PURE__ */ jsx(InlineField, { label, invalid: !!(internalDate.value && internalDate.invalid), className: styles.field, children: /* @__PURE__ */ jsx(
Input,
{
onChange: onChangeDate,
addonAfter: icon,
value: internalDate.value,
onBlur,
"data-testid": Components.DateTimePicker.input,
placeholder: t("grafana-ui.date-time-picker.select-placeholder", "Select date/time"),
ref,
suffix: clearable && internalDate.value && /* @__PURE__ */ jsx(Icon, { name: "times", className: styles.clearIcon, onClick: clearInternalDate })
}
) });
}
);
DateTimeInput.displayName = "DateTimeInput";
const DateTimeCalendar = React.forwardRef(
({
date,
onClose,
onChange,
isFullscreen,
maxDate,
minDate,
style,
showSeconds = true,
disabledHours,
disabledMinutes,
disabledSeconds,
timeZone
}, ref) => {
const calendarStyles = useStyles2(getBodyStyles);
const styles = useStyles2(getStyles);
const [timeOfDayDateTime, setTimeOfDayDateTime] = useState(() => {
if (date && date.isValid()) {
return dateTimeForTimeZone(getTimeZone({ timeZone }), date);
}
return dateTimeForTimeZone(getTimeZone({ timeZone }), /* @__PURE__ */ new Date());
});
const [reactCalendarDate, setReactCalendarDate] = useState(() => {
if (date && date.isValid()) {
return adjustDateForReactCalendar(date.toDate(), getTimeZone({ timeZone }));
}
return adjustDateForReactCalendar(/* @__PURE__ */ new Date(), getTimeZone({ timeZone }));
});
const onChangeDate = useCallback((date2) => {
if (date2 && !Array.isArray(date2)) {
setReactCalendarDate(date2);
}
}, []);
const onChangeTime = useCallback((date2) => {
setTimeOfDayDateTime(date2);
}, []);
const handleApply = () => {
const newDate = dateTime(timeOfDayDateTime);
newDate.set("date", reactCalendarDate.getDate());
newDate.set("month", reactCalendarDate.getMonth());
newDate.set("year", reactCalendarDate.getFullYear());
onChange(newDate);
};
return /* @__PURE__ */ jsxs("div", { className: cx(styles.container, { [styles.fullScreen]: isFullscreen }), style, ref, children: [
/* @__PURE__ */ jsx(
Calendar,
{
next2Label: null,
prev2Label: null,
value: reactCalendarDate,
nextLabel: /* @__PURE__ */ jsx(Icon, { name: "angle-right" }),
nextAriaLabel: t("grafana-ui.date-time-picker.next-label", "Next month"),
prevLabel: /* @__PURE__ */ jsx(Icon, { name: "angle-left" }),
prevAriaLabel: t("grafana-ui.date-time-picker.previous-label", "Previous month"),
onChange: onChangeDate,
locale: "en",
className: calendarStyles.body,
tileClassName: calendarStyles.title,
maxDate,
minDate
}
),
/* @__PURE__ */ jsx("div", { className: styles.time, children: /* @__PURE__ */ jsx(
TimeOfDayPicker,
{
showSeconds,
onChange: onChangeTime,
value: timeOfDayDateTime,
disabledHours,
disabledMinutes,
disabledSeconds
}
) }),
/* @__PURE__ */ jsxs(Stack, { children: [
/* @__PURE__ */ jsx(Button, { type: "button", onClick: handleApply, children: /* @__PURE__ */ jsx(Trans, { i18nKey: "grafana-ui.date-time-picker.apply", children: "Apply" }) }),
/* @__PURE__ */ jsx(Button, { variant: "secondary", type: "button", onClick: onClose, children: /* @__PURE__ */ jsx(Trans, { i18nKey: "grafana-ui.date-time-picker.cancel", children: "Cancel" }) })
] })
] });
}
);
DateTimeCalendar.displayName = "DateTimeCalendar";
const getStyles = (theme) => ({
container: css({
padding: theme.spacing(1),
border: `1px ${theme.colors.border.weak} solid`,
borderRadius: theme.shape.radius.default,
backgroundColor: theme.colors.background.primary,
zIndex: theme.zIndex.modal
}),
fullScreen: css({
position: "absolute"
}),
time: css({
marginBottom: theme.spacing(2)
}),
modal: css({
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: theme.zIndex.modal,
maxWidth: "280px"
}),
clearIcon: css({
cursor: "pointer"
}),
field: css({
marginBottom: 0,
width: "100%"
})
});
//# sourceMappingURL=DateTimePicker.mjs.map