import { GrafanaThemeV2, isDateTime, TimeOption, TimeRange, TimeZone } from '@grafana/data'; import { css, cx } from '@emotion/css'; import React, { memo, useState } from 'react'; import { useMedia } from 'react-use'; import { stylesFactory, useTheme2 } from '../../../themes'; import { CustomScrollbar } from '../../CustomScrollbar/CustomScrollbar'; import { Icon } from '../../Icon/Icon'; import { mapRangeToTimeOption } from './mapper'; import { TimePickerTitle } from './TimePickerTitle'; import { TimeRangeForm } from './TimeRangeForm'; import { TimeRangeList } from './TimeRangeList'; import { TimePickerFooter } from './TimePickerFooter'; const getStyles = stylesFactory((theme: GrafanaThemeV2, isReversed, hideQuickRanges, isContainerTall) => { return { container: css` background: ${theme.colors.background.secondary}; box-shadow: ${theme.shadows.z4}; position: absolute; z-index: ${theme.zIndex.dropdown}; width: 546px; top: 116%; border-radius: 2px; border: 1px solid ${theme.colors.border.weak}; ${isReversed ? 'left' : 'right'}: 0; @media only screen and (max-width: ${theme.breakpoints.values.lg}px) { width: 262px; } `, body: css` display: flex; height: ${isContainerTall ? '381px' : '217px'}; `, leftSide: css` display: flex; flex-direction: column; border-right: ${isReversed ? 'none' : `1px solid ${theme.colors.border.weak}`}; width: ${!hideQuickRanges ? '60%' : '100%'}; overflow: hidden; order: ${isReversed ? 1 : 0}; `, rightSide: css` width: 40% !important; border-right: ${isReversed ? `1px solid ${theme.colors.border.weak}` : 'none'}; @media only screen and (max-width: ${theme.breakpoints.values.lg}px) { width: 100% !important; } `, spacing: css` margin-top: 16px; `, }; }); const getNarrowScreenStyles = stylesFactory((theme: GrafanaThemeV2) => { return { header: css` display: flex; flex-direction: row; justify-content: space-between; align-items: center; border-bottom: 1px solid ${theme.colors.border.weak}; padding: 7px 9px 7px 9px; `, body: css` border-bottom: 1px solid ${theme.colors.border.weak}; `, form: css` padding: 7px 9px 7px 9px; `, }; }); const getFullScreenStyles = stylesFactory((theme: GrafanaThemeV2, hideQuickRanges?: boolean) => { return { container: css` padding-top: 9px; padding-left: 11px; padding-right: ${!hideQuickRanges ? '20%' : '11px'}; `, title: css` margin-bottom: 11px; `, recent: css` flex-grow: 1; display: flex; flex-direction: column; justify-content: flex-end; padding-top: ${theme.spacing(1)}; `, }; }); const getEmptyListStyles = stylesFactory((theme: GrafanaThemeV2) => { return { container: css` padding: 12px; margin: 12px; a, span { font-size: 13px; } `, link: css` color: ${theme.colors.text.link}; `, }; }); interface Props { value: TimeRange; onChange: (timeRange: TimeRange) => void; onChangeTimeZone: (timeZone: TimeZone) => void; timeZone?: TimeZone; quickOptions?: TimeOption[]; otherOptions?: TimeOption[]; history?: TimeRange[]; showHistory?: boolean; className?: string; hideTimeZone?: boolean; /** Reverse the order of relative and absolute range pickers. Used to left align the picker in forms */ isReversed?: boolean; hideQuickRanges?: boolean; } export interface PropsWithScreenSize extends Props { isFullscreen: boolean; } interface FormProps extends Omit { historyOptions?: TimeOption[]; } export const TimePickerContentWithScreenSize: React.FC = (props) => { const { quickOptions = [], otherOptions = [], isReversed, isFullscreen, hideQuickRanges, timeZone, value, onChange, history, showHistory, className, hideTimeZone, onChangeTimeZone, } = props; const isHistoryEmpty = !history?.length; const isContainerTall = (isFullscreen && showHistory) || (!isFullscreen && ((showHistory && !isHistoryEmpty) || !hideQuickRanges)); const theme = useTheme2(); const styles = getStyles(theme, isReversed, hideQuickRanges, isContainerTall); const historyOptions = mapToHistoryOptions(history, timeZone); return (
{isFullscreen && (
)} {(!isFullscreen || !hideQuickRanges) && ( {!isFullscreen && } {!hideQuickRanges && ( <>
)} )}
{!hideTimeZone && isFullscreen && }
); }; export const TimePickerContent: React.FC = (props) => { const theme = useTheme2(); const isFullscreen = useMedia(`(min-width: ${theme.breakpoints.values.lg}px)`); return ; }; const NarrowScreenForm: React.FC = (props) => { const { value, hideQuickRanges, onChange, timeZone, historyOptions = [], showHistory } = props; const theme = useTheme2(); const styles = getNarrowScreenStyles(theme); const isAbsolute = isDateTime(value.raw.from) || isDateTime(value.raw.to); const [collapsedFlag, setCollapsedFlag] = useState(!isAbsolute); const collapsed = hideQuickRanges ? false : collapsedFlag; return ( <>
{ if (!hideQuickRanges) { setCollapsedFlag(!collapsed); } }} > Absolute time range {!hideQuickRanges && }
{!collapsed && (
{showHistory && ( )}
)} ); }; const FullScreenForm: React.FC = (props) => { const theme = useTheme2(); const styles = getFullScreenStyles(theme, props.hideQuickRanges); return ( <>
Absolute time range
{props.showHistory && (
} timeZone={props.timeZone} />
)} ); }; const EmptyRecentList = memo(() => { const theme = useTheme2(); const styles = getEmptyListStyles(theme); return (
It looks like you haven't used this time picker before. As soon as you enter some time intervals, recently used intervals will appear here.
Read the documentation to find out more about how to enter custom time ranges.
); }); function mapToHistoryOptions(ranges?: TimeRange[], timeZone?: TimeZone): TimeOption[] { if (!Array.isArray(ranges) || ranges.length === 0) { return []; } return ranges.slice(ranges.length - 4).map((range) => mapRangeToTimeOption(range, timeZone)); } EmptyRecentList.displayName = 'EmptyRecentList';