UNPKG

@grafana/ui

Version:
259 lines (256 loc) • 8.77 kB
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