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 (
)
})}
)
}