UNPKG

@grafana/ui

Version:
335 lines (327 loc) • 12.1 kB
'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 Calendar = require('react-calendar'); var reactUse = require('react-use'); var data = require('@grafana/data'); var e2eSelectors = require('@grafana/e2e-selectors'); var i18n = require('@grafana/i18n'); var ThemeContext = require('../../../themes/ThemeContext.cjs'); var floating = require('../../../utils/floating.cjs'); var Button = require('../../Button/Button.cjs'); var InlineField = require('../../Forms/InlineField.cjs'); var Icon = require('../../Icon/Icon.cjs'); var Input = require('../../Input/Input.cjs'); var Stack = require('../../Layout/Stack/Stack.cjs'); var getModalStyles = require('../../Modal/getModalStyles.cjs'); var Portal = require('../../Portal/Portal.cjs'); var TimeOfDayPicker = require('../TimeOfDayPicker.cjs'); var CalendarBody = require('../TimeRangePicker/CalendarBody.cjs'); var utils = require('../utils.cjs'); var adjustDateForReactCalendar = require('../utils/adjustDateForReactCalendar.cjs'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceCompat(React); var Calendar__default = /*#__PURE__*/_interopDefaultCompat(Calendar); "use strict"; const DateTimePicker = ({ date, maxDate, minDate, label, onChange, disabledHours, disabledMinutes, disabledSeconds, timeZone, showSeconds = true, clearable = false }) => { const [isOpen, setOpen] = React.useState(false); const ref = React.useRef(null); const { overlayProps, underlayProps } = overlays.useOverlay( { onClose: () => setOpen(false), isDismissable: true, isOpen, shouldCloseOnInteractOutside: (element) => !Portal.getPortalContainer().contains(element) }, ref ); const { dialogProps } = dialog.useDialog({}, ref); const theme = ThemeContext.useTheme2(); const { modalBackdrop } = ThemeContext.useStyles2(getModalStyles.getModalStyles); const isFullscreen = reactUse.useMedia(`(min-width: ${theme.breakpoints.values.lg}px)`); const placement = "bottom-start"; const styles = ThemeContext.useStyles2(getStyles); const middleware = floating.getPositioningMiddleware(placement); const { refs, floatingStyles } = react.useFloating({ open: isOpen, placement, onOpenChange: setOpen, middleware, whileElementsMounted: react.autoUpdate, strategy: "fixed" }); const onApply = React.useCallback( (date2) => { setOpen(false); onChange(date2); }, [onChange] ); const onOpen = React.useCallback( (event) => { event.preventDefault(); setOpen(true); }, [setOpen] ); return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-testid": "date-time-picker", style: { position: "relative" }, children: [ /* @__PURE__ */ jsxRuntime.jsx( DateTimeInput, { date, onChange, isFullscreen, onOpen, label, ref: refs.setReference, showSeconds, clearable, timeZone } ), isOpen ? isFullscreen ? /* @__PURE__ */ jsxRuntime.jsx(Portal.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(focus.FocusScope, { contain: true, autoFocus: true, restoreFocus: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, ...overlayProps, ...dialogProps, children: /* @__PURE__ */ jsxRuntime.jsx( DateTimeCalendar, { date, onChange: onApply, isFullscreen: true, onClose: () => setOpen(false), maxDate, minDate, ref: refs.setFloating, style: floatingStyles, showSeconds, disabledHours, disabledMinutes, disabledSeconds, timeZone } ) }) }) }) : /* @__PURE__ */ jsxRuntime.jsxs(Portal.Portal, { children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: modalBackdrop, ...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.modal, children: /* @__PURE__ */ jsxRuntime.jsx( DateTimeCalendar, { date, maxDate, minDate, onChange: onApply, isFullscreen: false, onClose: () => setOpen(false), showSeconds, disabledHours, disabledMinutes, disabledSeconds, timeZone } ) }) }) }) ] }) : null ] }); }; const DateTimeInput = React__namespace.forwardRef( ({ date, label, onChange, onOpen, timeZone, showSeconds = true, clearable = false }, ref) => { const styles = ThemeContext.useStyles2(getStyles); const format = showSeconds ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD HH:mm"; const [internalDate, setInternalDate] = React.useState(() => { return { value: date ? data.dateTimeFormat(date, { timeZone }) : !clearable ? data.dateTimeFormat(data.dateTime(), { timeZone }) : "", invalid: false }; }); React.useEffect(() => { if (date) { const formattedDate = data.dateTimeFormat(date, { format, timeZone }); setInternalDate({ invalid: !utils.isValid(formattedDate), value: data.isDateTime(date) ? formattedDate : date }); } }, [date, format, timeZone]); const onChangeDate = React.useCallback((event) => { const isInvalid = !utils.isValid(event.currentTarget.value); setInternalDate({ value: event.currentTarget.value, invalid: isInvalid }); }, []); const onBlur = React.useCallback(() => { if (!internalDate.invalid && internalDate.value) { const date2 = data.dateTimeForTimeZone(data.getTimeZone({ timeZone }), internalDate.value); onChange(date2); } }, [internalDate, onChange, timeZone]); const clearInternalDate = React.useCallback(() => { setInternalDate({ value: "", invalid: false }); onChange(); }, [onChange]); const icon = /* @__PURE__ */ jsxRuntime.jsx( Button.Button, { "aria-label": i18n.t("grafana-ui.date-time-picker.calendar-icon-label", "Time picker"), icon: "calendar-alt", variant: "secondary", onClick: onOpen } ); return /* @__PURE__ */ jsxRuntime.jsx(InlineField.InlineField, { label, invalid: !!(internalDate.value && internalDate.invalid), className: styles.field, children: /* @__PURE__ */ jsxRuntime.jsx( Input.Input, { onChange: onChangeDate, addonAfter: icon, value: internalDate.value, onBlur, "data-testid": e2eSelectors.Components.DateTimePicker.input, placeholder: i18n.t("grafana-ui.date-time-picker.select-placeholder", "Select date/time"), ref, suffix: clearable && internalDate.value && /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: "times", className: styles.clearIcon, onClick: clearInternalDate }) } ) }); } ); DateTimeInput.displayName = "DateTimeInput"; const DateTimeCalendar = React__namespace.forwardRef( ({ date, onClose, onChange, isFullscreen, maxDate, minDate, style, showSeconds = true, disabledHours, disabledMinutes, disabledSeconds, timeZone }, ref) => { const calendarStyles = ThemeContext.useStyles2(CalendarBody.getBodyStyles); const styles = ThemeContext.useStyles2(getStyles); const [timeOfDayDateTime, setTimeOfDayDateTime] = React.useState(() => { if (date && date.isValid()) { return data.dateTimeForTimeZone(data.getTimeZone({ timeZone }), date); } return data.dateTimeForTimeZone(data.getTimeZone({ timeZone }), /* @__PURE__ */ new Date()); }); const [reactCalendarDate, setReactCalendarDate] = React.useState(() => { if (date && date.isValid()) { return adjustDateForReactCalendar.adjustDateForReactCalendar(date.toDate(), data.getTimeZone({ timeZone })); } return adjustDateForReactCalendar.adjustDateForReactCalendar(/* @__PURE__ */ new Date(), data.getTimeZone({ timeZone })); }); const onChangeDate = React.useCallback((date2) => { if (date2 && !Array.isArray(date2)) { setReactCalendarDate(date2); } }, []); const onChangeTime = React.useCallback((date2) => { setTimeOfDayDateTime(date2); }, []); const handleApply = () => { const newDate = data.dateTime(timeOfDayDateTime); newDate.set("date", reactCalendarDate.getDate()); newDate.set("month", reactCalendarDate.getMonth()); newDate.set("year", reactCalendarDate.getFullYear()); onChange(newDate); }; return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: css.cx(styles.container, { [styles.fullScreen]: isFullscreen }), style, ref, children: [ /* @__PURE__ */ jsxRuntime.jsx( Calendar__default.default, { next2Label: null, prev2Label: null, value: reactCalendarDate, nextLabel: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: "angle-right" }), nextAriaLabel: i18n.t("grafana-ui.date-time-picker.next-label", "Next month"), prevLabel: /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: "angle-left" }), prevAriaLabel: i18n.t("grafana-ui.date-time-picker.previous-label", "Previous month"), onChange: onChangeDate, locale: "en", className: calendarStyles.body, tileClassName: calendarStyles.title, maxDate, minDate } ), /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.time, children: /* @__PURE__ */ jsxRuntime.jsx( TimeOfDayPicker.TimeOfDayPicker, { showSeconds, onChange: onChangeTime, value: timeOfDayDateTime, disabledHours } ) }), /* @__PURE__ */ jsxRuntime.jsxs(Stack.Stack, { children: [ /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { type: "button", onClick: handleApply, children: /* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "grafana-ui.date-time-picker.apply", children: "Apply" }) }), /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "secondary", type: "button", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(i18n.Trans, { i18nKey: "grafana-ui.date-time-picker.cancel", children: "Cancel" }) }) ] }) ] }); } ); DateTimeCalendar.displayName = "DateTimeCalendar"; const getStyles = (theme) => ({ container: css.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.css({ position: "absolute" }), time: css.css({ marginBottom: theme.spacing(2) }), modal: css.css({ position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)", zIndex: theme.zIndex.modal, maxWidth: "280px" }), clearIcon: css.css({ cursor: "pointer" }), field: css.css({ marginBottom: 0, width: "100%" }) }); exports.DateTimePicker = DateTimePicker; //# sourceMappingURL=DateTimePicker.cjs.map