UNPKG

@grafana/ui

Version:
246 lines (243 loc) • 8.48 kB
import { jsx, jsxs } from 'react/jsx-runtime'; import { css } from '@emotion/css'; import { useState, useId, useEffect, useCallback } from 'react'; 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 { commonFormat } from '../commonFormat.mjs'; import { isValid } from '../utils.mjs'; import TimePickerCalendar from './TimePickerCalendar.mjs'; "use strict"; const ERROR_MESSAGES = { default: () => t("time-picker.range-content.default-error", 'Please enter a past date or "{{now}}"', { now: "now" }), range: () => t("time-picker.range-content.range-error", `"From" can't be after "To"`) }; const TimeRangeContent = (props) => { const { value, isFullscreen = false, timeZone, onApply: onApplyFromProps, isReversed, fiscalYearStartMonth, onError, weekStart } = props; const [fromValue, toValue] = valueToState(value.raw.from, value.raw.to, timeZone); const style = useStyles2(getStyles); const [from, setFrom] = useState(fromValue); const [to, setTo] = useState(toValue); const [isOpen, setOpen] = useState(false); const fromFieldId = useId(); const toFieldId = useId(); useEffect(() => { const [fromValue2, toValue2] = valueToState(value.raw.from, value.raw.to, timeZone); setFrom(fromValue2); setTo(toValue2); }, [value.raw.from, value.raw.to, timeZone]); const onOpen = useCallback( (event) => { event.preventDefault(); setOpen(true); }, [setOpen] ); const onApply = useCallback(() => { if (to.invalid || from.invalid) { return; } const raw = { from: from.value, to: to.value }; const timeRange = rangeUtil.convertRawToRange(raw, timeZone, fiscalYearStartMonth, commonFormat); onApplyFromProps(timeRange); }, [from.invalid, from.value, onApplyFromProps, timeZone, to.invalid, to.value, fiscalYearStartMonth]); const onChange = useCallback( (from2, to2) => { const [fromValue2, toValue2] = valueToState(from2, to2, timeZone); setFrom(fromValue2); setTo(toValue2); }, [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; } const [fromValue2, toValue2] = valueToState(range.from, range.to, timeZone); setFrom(fromValue2); setTo(toValue2); }; 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: from.invalid, error: from.errorMessage, children: /* @__PURE__ */ jsx( Input, { id: fromFieldId, onClick: (event) => event.stopPropagation(), onChange: (event) => onChange(event.currentTarget.value, to.value), addonAfter: icon, onKeyDown: submitOnEnter, "data-testid": selectors.components.TimePicker.fromField, value: from.value } ) } ), fyTooltip ] }), /* @__PURE__ */ jsxs("div", { className: style.fieldContainer, children: [ /* @__PURE__ */ jsx(Field, { label: t("time-picker.range-content.to-input", "To"), invalid: to.invalid, error: to.errorMessage, children: /* @__PURE__ */ jsx( Input, { id: toFieldId, onClick: (event) => event.stopPropagation(), onChange: (event) => onChange(from.value, event.currentTarget.value), addonAfter: icon, onKeyDown: submitOnEnter, "data-testid": selectors.components.TimePicker.toField, value: to.value } ) }), 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(from.value, { timeZone }), to: dateTimeParse(to.value, { timeZone }), onApply, onClose: () => setOpen(false), onChange, timeZone, isReversed, weekStart } ) ] }); }; function isRangeInvalid(from, to, timezone) { const raw = { from, to }; const timeRange = rangeUtil.convertRawToRange(raw, timezone, void 0, commonFormat); const valid = timeRange.from.isSame(timeRange.to) || timeRange.from.isBefore(timeRange.to); return !valid; } function valueToState(rawFrom, rawTo, timeZone) { const fromValue = valueAsString(rawFrom, timeZone); const toValue = valueAsString(rawTo, timeZone); const fromInvalid = !isValid(fromValue, false, timeZone); const toInvalid = !isValid(toValue, true, timeZone); const rangeInvalid = isRangeInvalid(fromValue, toValue, timeZone) && !toInvalid; return [ { value: fromValue, invalid: fromInvalid || rangeInvalid, errorMessage: rangeInvalid && !fromInvalid ? ERROR_MESSAGES.range() : ERROR_MESSAGES.default() }, { value: toValue, invalid: toInvalid, errorMessage: ERROR_MESSAGES.default() } ]; } function valueAsString(value, timeZone) { if (isDateTime(value)) { return dateTimeFormat(value, { timeZone, format: commonFormat }); } if (value.endsWith("Z")) { const dt = dateTimeParse(value); return dateTimeFormat(dt, { timeZone, format: commonFormat }); } 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