import dayjs from 'dayjs' import { ArrowRight, SearchIcon } from 'lucide-react' import { ReactNode, useEffect, useMemo, useState } from 'react' import { cn, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from 'ui' import { ChartHighlight } from './useChartHighlight' import { useFormatDateTime } from '@/lib/datetime' export type UpdateDateRange = (from: string, to: string) => void export type ChartHighlightActionContext = { start: string end: string clear: () => void chartId?: string } export type ChartHighlightAction = { id: string label: string | ((ctx: ChartHighlightActionContext) => string) icon?: ReactNode isDisabled?: (ctx: ChartHighlightActionContext) => boolean rightSlot?: ReactNode | ((ctx: ChartHighlightActionContext) => ReactNode) onSelect: (ctx: ChartHighlightActionContext) => void } export const ChartHighlightActions = ({ chartHighlight, updateDateRange, actions, chartId, }: { chartHighlight?: ChartHighlight updateDateRange?: UpdateDateRange actions?: ChartHighlightAction[] chartId?: string }) => { const { left: selectedRangeStart, right: selectedRangeEnd, clearHighlight } = chartHighlight ?? {} const [isOpen, setIsOpen] = useState(!!chartHighlight?.popoverPosition) const formatChartDate = useFormatDateTime() useEffect(() => { setIsOpen(!!chartHighlight?.popoverPosition && selectedRangeStart !== selectedRangeEnd) }, [chartHighlight?.popoverPosition]) const ctx: ChartHighlightActionContext | undefined = selectedRangeStart && selectedRangeEnd && clearHighlight ? { start: selectedRangeStart, end: selectedRangeEnd, clear: clearHighlight, chartId } : undefined const defaultActions: ChartHighlightAction[] = useMemo(() => { if (!updateDateRange || !ctx) return [] const isDisabled = dayjs(ctx.end).diff(dayjs(ctx.start), 'minutes') < 10 return [ { id: 'zoom-in', label: 'Zoom in', icon: , rightSlot: isDisabled ? Min. 10 minutes : null, isDisabled: () => isDisabled, onSelect: ({ start, end, clear }) => { if (isDisabled) return updateDateRange(start, end) clear() }, }, ] }, [ctx, updateDateRange]) const allActions: ChartHighlightAction[] = useMemo(() => { const provided = actions ?? [] return [...defaultActions, ...provided] }, [defaultActions, actions]) return ( clearHighlight?.()} onInteractOutside={(e) => { const target = e.target as Element | null // If the user clicked on a chart, handleMouseDown will manage the new selection. // Calling clearHighlight here would race with it and clobber the new state. if (target?.closest('.recharts-wrapper')) return clearHighlight?.() }} > {formatChartDate(selectedRangeStart!, 'MMM D, H:mm')} {formatChartDate(selectedRangeEnd!, 'MMM D, H:mm')} {allActions.map((action) => { const disabled = ctx && action.isDisabled ? action.isDisabled(ctx) : false let labelNode: ReactNode = null if (typeof action.label === 'function') { labelNode = ctx ? action.label(ctx) : null } else { labelNode = action.label } let rightNode: ReactNode = null if (typeof action.rightSlot === 'function') { rightNode = ctx ? action.rightSlot(ctx) : null } else { rightNode = action.rightSlot ?? null } return ( ) })} ) }