@grafana/ui
Version:
Grafana Components Library
259 lines (254 loc) • 10 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var css = require('@emotion/css');
var react = require('@floating-ui/react');
var dialog = require('@react-aria/dialog');
var focus = require('@react-aria/focus');
var overlays = require('@react-aria/overlays');
var React = require('react');
var i18n = require('@grafana/i18n');
var ThemeContext = require('../../../themes/ThemeContext.cjs');
var floating = require('../../../utils/floating.cjs');
var Button = require('../../Button/Button.cjs');
var Field = require('../../Forms/Field.cjs');
var Icon = require('../../Icon/Icon.cjs');
var Input = require('../../Input/Input.cjs');
var ScrollContainer = require('../../ScrollContainer/ScrollContainer.cjs');
var TimePickerTitle = require('../TimeRangePicker/TimePickerTitle.cjs');
var TimeRangeList = require('../TimeRangePicker/TimeRangeList.cjs');
var options = require('../options.cjs');
var utils = require('./utils.cjs');
"use strict";
function RelativeTimeRangePicker(props) {
const { timeRange, onChange } = props;
const [isOpen, setIsOpen] = React.useState(false);
const onClose = React.useCallback(() => setIsOpen(false), []);
const timeOption = utils.mapRelativeTimeRangeToOption(timeRange);
const [from, setFrom] = React.useState({ value: timeOption.from, validation: utils.isRangeValid(timeOption.from) });
const [to, setTo] = React.useState({ value: timeOption.to, validation: utils.isRangeValid(timeOption.to) });
const ref = React.useRef(null);
const { overlayProps, underlayProps } = overlays.useOverlay(
{ onClose: () => setIsOpen(false), isDismissable: true, isOpen },
ref
);
const { dialogProps } = dialog.useDialog({}, ref);
const validOptions = options.getQuickOptions().filter((o) => utils.isRelativeFormat(o.from));
const placement = "bottom-start";
const middleware = floating.getPositioningMiddleware(placement);
const { context, refs, floatingStyles } = react.useFloating({
open: isOpen,
placement,
onOpenChange: setIsOpen,
middleware,
whileElementsMounted: react.autoUpdate,
strategy: "fixed"
});
const click = react.useClick(context);
const dismiss = react.useDismiss(context);
const { getReferenceProps, getFloatingProps } = react.useInteractions([dismiss, click]);
const styles = ThemeContext.useStyles2(getStyles(from.validation.errorMessage, to.validation.errorMessage));
const onChangeTimeOption = (option) => {
const relativeTimeRange = utils.mapOptionToRelativeTimeRange(option);
if (!relativeTimeRange) {
return;
}
onClose();
setFrom({ ...from, value: option.from });
setTo({ ...to, value: option.to });
onChange(relativeTimeRange);
};
const onOpen = React.useCallback(
(event) => {
event.stopPropagation();
event.preventDefault();
setIsOpen(!isOpen);
},
[isOpen]
);
const onApply = (event) => {
event.preventDefault();
if (!to.validation.isValid || !from.validation.isValid) {
return;
}
const timeRange2 = utils.mapOptionToRelativeTimeRange({
from: from.value,
to: to.value,
display: ""
});
if (!timeRange2) {
return;
}
onChange(timeRange2);
setIsOpen(false);
};
const { from: timeOptionFrom, to: timeOptionTo } = timeOption;
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.container, children: [
/* @__PURE__ */ jsxRuntime.jsxs(
"button",
{
ref: refs.setReference,
className: styles.pickerInput,
type: "button",
onClick: onOpen,
...getReferenceProps(),
children: [
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.clockIcon, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: "clock-nine" }) }),
/* @__PURE__ */ jsxRuntime.jsx("span", { children: /* @__PURE__ */ jsxRuntime.jsxs(i18n.Trans, { i18nKey: "time-picker.time-range.from-to", children: [
{ timeOptionFrom },
" to ",
{ timeOptionTo }
] }) }),
/* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.caretIcon, children: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: isOpen ? "angle-up" : "angle-down", size: "lg" }) })
]
}
),
isOpen && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "presentation", className: styles.backdrop, ...underlayProps }),
/* @__PURE__ */ jsxRuntime.jsx(focus.FocusScope, { contain: true, autoFocus: true, restoreFocus: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, ...overlayProps, ...dialogProps, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.content, ref: refs.setFloating, style: floatingStyles, ...getFloatingProps(), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.body, children: [
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.leftSide, children: /* @__PURE__ */ jsxRuntime.jsx(ScrollContainer.ScrollContainer, { showScrollIndicators: true, children: /* @__PURE__ */ jsxRuntime.jsx(
TimeRangeList.TimeRangeList,
{
title: i18n.t("time-picker.time-range.example-title", "Example time ranges"),
options: validOptions,
onChange: onChangeTimeOption,
value: timeOption
}
) }) }),
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.rightSide, children: [
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.title, children: /* @__PURE__ */ jsxRuntime.jsx(TimePickerTitle.TimePickerTitle, { children: /* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "time-picker.time-range.specify", children: "Specify time range" }) }) }),
/* @__PURE__ */ jsxRuntime.jsx(
Field.Field,
{
label: i18n.t("time-picker.time-range.from-label", "From"),
invalid: !from.validation.isValid,
error: from.validation.errorMessage,
children: /* @__PURE__ */ jsxRuntime.jsx(
Input.Input,
{
onClick: (event) => event.stopPropagation(),
onBlur: () => setFrom({ ...from, validation: utils.isRangeValid(from.value) }),
onChange: (event) => setFrom({ ...from, value: event.currentTarget.value }),
value: from.value
}
)
}
),
/* @__PURE__ */ jsxRuntime.jsx(
Field.Field,
{
label: i18n.t("time-picker.time-range.to-label", "To"),
invalid: !to.validation.isValid,
error: to.validation.errorMessage,
children: /* @__PURE__ */ jsxRuntime.jsx(
Input.Input,
{
onClick: (event) => event.stopPropagation(),
onBlur: () => setTo({ ...to, validation: utils.isRangeValid(to.value) }),
onChange: (event) => setTo({ ...to, value: event.currentTarget.value }),
value: to.value
}
)
}
),
/* @__PURE__ */ jsxRuntime.jsx(
Button.Button,
{
"aria-label": i18n.t("time-picker.time-range.submit-button-label", "TimePicker submit button"),
onClick: onApply,
children: /* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "time-picker.time-range.apply", children: "Apply time range" })
}
)
] })
] }) }) }) })
] })
] });
}
const getStyles = (fromError, toError) => (theme) => {
const inputStyles = Input.getInputStyles({ theme, invalid: false });
const bodyMinimumHeight = 250;
const bodyHeight = bodyMinimumHeight + calculateErrorHeight(theme, fromError) + calculateErrorHeight(theme, toError);
return {
backdrop: css.css({
position: "fixed",
zIndex: theme.zIndex.modalBackdrop,
top: 0,
right: 0,
bottom: 0,
left: 0
}),
container: css.css({
display: "flex",
position: "relative"
}),
pickerInput: css.cx(
inputStyles.input,
inputStyles.wrapper,
css.css({
display: "flex",
alignItems: "center",
justifyContent: "space-between",
cursor: "pointer",
paddingRight: 0,
paddingLeft: 0,
lineHeight: `${theme.spacing.gridSize * theme.components.height.md - 2}px`
})
),
caretIcon: css.cx(
inputStyles.suffix,
css.css({
position: "relative",
marginLeft: theme.spacing(0.5)
})
),
clockIcon: css.cx(
inputStyles.prefix,
css.css({
position: "relative",
marginRight: theme.spacing(0.5)
})
),
content: css.css({
background: theme.colors.background.primary,
boxShadow: theme.shadows.z3,
position: "absolute",
zIndex: theme.zIndex.modal,
width: "500px",
top: "100%",
borderRadius: theme.shape.radius.default,
border: `1px solid ${theme.colors.border.weak}`,
left: 0,
whiteSpace: "normal"
}),
body: css.css({
display: "flex",
height: `${bodyHeight}px`
}),
description: css.css({
color: theme.colors.text.secondary,
fontSize: theme.typography.size.sm
}),
leftSide: css.css({
width: "50% !important",
borderRight: `1px solid ${theme.colors.border.medium}`
}),
rightSide: css.css({
width: "50%",
padding: theme.spacing(1)
}),
title: css.css({
marginBottom: theme.spacing(1)
})
};
};
function calculateErrorHeight(theme, errorMessage) {
if (!errorMessage) {
return 0;
}
if (errorMessage.length > 34) {
return theme.spacing.gridSize * 6.5;
}
return theme.spacing.gridSize * 4;
}
exports.RelativeTimeRangePicker = RelativeTimeRangePicker;
//# sourceMappingURL=RelativeTimeRangePicker.cjs.map