@grafana/ui
Version:
Grafana Components Library
259 lines (256 loc) • 8.77 kB
JavaScript
import { jsx, jsxs } from 'react/jsx-runtime';
import { css } from '@emotion/css';
import { useState, useId, useEffect, useCallback } from 'react';
import { useForm } from 'react-hook-form';
import { rangeUtil, dateTimeParse, isDateTime, dateTimeFormat } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t, Trans } from '@grafana/i18n';
import { useStyles2 } from '../../../themes/ThemeContext.mjs';
import { Button } from '../../Button/Button.mjs';
import { Field } from '../../Forms/Field.mjs';
import { Icon } from '../../Icon/Icon.mjs';
import { Input } from '../../Input/Input.mjs';
import { Tooltip } from '../../Tooltip/Tooltip.mjs';
import { isValid } from '../utils.mjs';
import TimePickerCalendar from './TimePickerCalendar.mjs';
"use strict";
const ERROR_MESSAGES = {
default: () => t(
"time-picker.range-content.default-error",
"Enter a date ({{dateExample}}) or relative time ({{relativeTimeExample1}}, {{relativeTimeExample2}})",
{
dateExample: "YYYY-MM-DD HH:mm:ss",
relativeTimeExample1: "now",
relativeTimeExample2: "now-1h"
}
),
range: () => t("time-picker.range-content.range-error", '"From" date must be before "To" date')
};
const TimeRangeContent = (props) => {
var _a, _b;
const {
value,
isFullscreen = false,
timeZone,
onApply: onApplyFromProps,
isReversed,
fiscalYearStartMonth,
onError,
weekStart
} = props;
const style = useStyles2(getStyles);
const [isOpen, setOpen] = useState(false);
const {
handleSubmit,
register,
formState: { errors },
setValue,
watch
} = useForm({
defaultValues: {
from: valueAsString(value.raw.from, timeZone),
to: valueAsString(value.raw.to, timeZone)
}
});
const fromFieldId = useId();
const toFieldId = useId();
useEffect(() => {
setValue("from", valueAsString(value.raw.from, timeZone));
setValue("to", valueAsString(value.raw.to, timeZone));
}, [value.raw.from, value.raw.to, setValue, timeZone]);
const onOpen = () => setOpen(true);
const onApply = useCallback(() => {
handleSubmit((data) => {
const raw = { from: data.from, to: data.to };
const timeRange = rangeUtil.convertRawToRange(raw, timeZone, fiscalYearStartMonth);
onApplyFromProps(timeRange);
})();
}, [handleSubmit, timeZone, fiscalYearStartMonth, onApplyFromProps]);
const onChange = useCallback(
(from, to) => {
setValue("from", valueAsString(from, timeZone));
setValue("to", valueAsString(to, timeZone));
},
[setValue, timeZone]
);
const submitOnEnter = (event) => {
if (event.key === "Enter") {
onApply();
}
};
const onCopy = () => {
const rawSource = value.raw;
const clipboardPayload = rangeUtil.formatRawTimeRange(rawSource);
navigator.clipboard.writeText(JSON.stringify(clipboardPayload));
};
const onPaste = async () => {
const raw = await navigator.clipboard.readText();
let range;
try {
range = JSON.parse(raw);
} catch (error) {
if (onError) {
onError(raw);
}
return;
}
setValue("from", valueAsString(range.from, timeZone));
setValue("to", valueAsString(range.to, timeZone));
};
const fiscalYear = rangeUtil.convertRawToRange({ from: "now/fy", to: "now/fy" }, timeZone, fiscalYearStartMonth);
const fyTooltip = /* @__PURE__ */ jsx("div", { className: style.tooltip, children: rangeUtil.isFiscal(value) ? /* @__PURE__ */ jsx(
Tooltip,
{
content: t("time-picker.range-content.fiscal-year", "Fiscal year: {{from}} - {{to}}", {
from: fiscalYear.from.format("MMM-DD"),
to: fiscalYear.to.format("MMM-DD")
}),
children: /* @__PURE__ */ jsx(Icon, { name: "info-circle" })
}
) : null });
const icon = /* @__PURE__ */ jsx(
Button,
{
"aria-label": t("time-picker.range-content.open-input-calendar", "Open calendar"),
"data-testid": selectors.components.TimePicker.calendar.openButton,
icon: "calendar-alt",
variant: "secondary",
type: "button",
onClick: onOpen
}
);
return /* @__PURE__ */ jsxs("div", { children: [
/* @__PURE__ */ jsxs("div", { className: style.fieldContainer, children: [
/* @__PURE__ */ jsx(
Field,
{
label: t("time-picker.range-content.from-input", "From"),
invalid: !!errors.from,
error: (_a = errors.from) == null ? void 0 : _a.message,
children: /* @__PURE__ */ jsx(
Input,
{
...register("from", {
required: ERROR_MESSAGES.default(),
validate: (value2, formValues) => {
if (!isValid(value2, false, timeZone)) {
return ERROR_MESSAGES.default();
}
if (!!formValues.to && isValid(formValues.to, true, timeZone) && isRangeInvalid(value2, formValues.to, timeZone)) {
return ERROR_MESSAGES.range();
}
return true;
}
}),
id: fromFieldId,
onClick: (event) => event.stopPropagation(),
onKeyDown: submitOnEnter,
addonAfter: icon,
"data-testid": selectors.components.TimePicker.fromField
}
)
}
),
fyTooltip
] }),
/* @__PURE__ */ jsxs("div", { className: style.fieldContainer, children: [
/* @__PURE__ */ jsx(Field, { label: t("time-picker.range-content.to-input", "To"), invalid: !!errors.to, error: (_b = errors.to) == null ? void 0 : _b.message, children: /* @__PURE__ */ jsx(
Input,
{
...register("to", {
required: ERROR_MESSAGES.default(),
validate: (value2, formValues) => {
if (!isValid(value2, true, timeZone)) {
return ERROR_MESSAGES.default();
}
if (!!formValues.from && isValid(formValues.from, false, timeZone) && isRangeInvalid(formValues.from, value2, timeZone)) {
return ERROR_MESSAGES.range();
}
return true;
}
}),
id: toFieldId,
onClick: (event) => event.stopPropagation(),
onKeyDown: submitOnEnter,
addonAfter: icon,
"data-testid": selectors.components.TimePicker.toField
}
) }),
fyTooltip
] }),
/* @__PURE__ */ jsxs("div", { className: style.buttonsContainer, children: [
/* @__PURE__ */ jsx(
Button,
{
"data-testid": selectors.components.TimePicker.copyTimeRange,
icon: "copy",
variant: "secondary",
tooltip: t("time-picker.copy-paste.tooltip-copy", "Copy time range to clipboard"),
type: "button",
onClick: onCopy
}
),
/* @__PURE__ */ jsx(
Button,
{
"data-testid": selectors.components.TimePicker.pasteTimeRange,
icon: "clipboard-alt",
variant: "secondary",
tooltip: t("time-picker.copy-paste.tooltip-paste", "Paste time range"),
type: "button",
onClick: onPaste
}
),
/* @__PURE__ */ jsx(Button, { "data-testid": selectors.components.TimePicker.applyTimeRange, type: "button", onClick: onApply, children: /* @__PURE__ */ jsx(Trans, { i18nKey: "time-picker.range-content.apply-button", children: "Apply time range" }) })
] }),
/* @__PURE__ */ jsx(
TimePickerCalendar,
{
isFullscreen,
isOpen,
from: dateTimeParse(watch("from"), { timeZone }),
to: dateTimeParse(watch("to"), { timeZone }),
onApply,
onClose: () => setOpen(false),
onChange,
timeZone,
isReversed,
weekStart
}
)
] });
};
function isRangeInvalid(from, to, timezone) {
const raw = { from, to };
const timeRange = rangeUtil.convertRawToRange(raw, timezone);
const valid = timeRange.from.isSame(timeRange.to) || timeRange.from.isBefore(timeRange.to);
return !valid;
}
function valueAsString(value, timeZone) {
if (isDateTime(value)) {
return dateTimeFormat(value, { timeZone });
}
if (value.endsWith("Z")) {
const dt = dateTimeParse(value);
return dateTimeFormat(dt, { timeZone });
}
return value;
}
function getStyles(theme) {
return {
fieldContainer: css({
display: "flex"
}),
buttonsContainer: css({
display: "flex",
gap: theme.spacing(0.5),
marginTop: theme.spacing(1)
}),
tooltip: css({
paddingLeft: theme.spacing(1),
paddingTop: theme.spacing(3)
})
};
}
export { TimeRangeContent };
//# sourceMappingURL=TimeRangeContent.mjs.map