import { Box, IconButton } from '@mui/material'
import { HighlightAltOutlined } from '@mui/icons-material'
import { useCallback, useEffect, useRef } from 'react'
import { widgetStoreActions } from '../../stores/widget-store'
import type { BrushSelectedItems, BrushState, BrushToggleProps } from './types'
import { styles } from './style'
import { Tooltip } from '../../../components'
import { useWidgetSelector } from '../../stores/use-widget-selector'
import { BrushOverlay } from './brush-overlay'
/**
* Kept for backwards compatibility. The tool is no longer registered with the
* widget store — brush state is now owned directly by this component and the
* associated DOM overlay instead of being driven through the config pipeline.
* Existing code that looks this constant up in `registeredTools` will simply
* find no match.
*/
export const BRUSH_TOGGLE_TOOL_ID = 'brush-toggle'
/**
* Widget action to toggle brush-style selection on bar and histogram widgets.
*
* Renders a toggle button and a portaled overlay rendered inside the chart's
* `
`. The overlay handles all pointer interactions, draws selection
* rectangles in DOM, and hit-tests against the widget data. All state lives
* in the widget store — rectangles survive config-pipeline re-renders
* (`setOption({ notMerge: true })`) automatically.
*
* Only intended for bar and histogram widgets (both use `xAxis.type === 'category'`).
*
* @example
* ```tsx
* console.log(items)}
* />
* ```
*/
export function BrushToggle({
id,
onBrushSelected,
multiBrush,
selections,
defaultEnabled = false,
labels,
Icon,
IconButtonProps,
}: BrushToggleProps) {
// Single subscription for everything this component cares about.
const { brush, selection } = useWidgetSelector(id, (w) => {
const s = w as BrushState | undefined
return {
brush: s?.brush ?? defaultEnabled,
selection: s?.brushSelection,
}
})
// Seed the widget store with `defaultEnabled` on first mount. Guarded so
// remounts don't stomp whatever the user has toggled to.
useEffect(() => {
const current = widgetStoreActions.getWidget(id)?.brush
if (current === undefined) {
widgetStoreActions.setWidget(id, { brush: defaultEnabled })
}
}, [id, defaultEnabled])
const handleToggle = useCallback(() => {
if (brush) {
// Disabling: keep rectangles visible so the consumer's selection stays
// in sync. Re-enabling clears them below (fresh slate each activation).
widgetStoreActions.setWidget(id, { brush: false })
} else {
widgetStoreActions.setWidget(id, {
brush: true,
brushRects: [],
brushSelection: { dataIndex: [], seriesIndex: 0 },
})
// Mirror the clear to the consumer so its selection drops with the
// visual rectangles.
onBrushSelected?.({ dataIndex: [], seriesIndex: 0 })
}
}, [brush, id, onBrushSelected])
// Invoke the consumer's callback whenever the overlay writes a new
// selection to the store. `selection` is a new object reference on each
// pointerup so a simple reference check is enough.
const lastSelectionRef = useRef(undefined)
useEffect(() => {
if (!selection) return
if (lastSelectionRef.current === selection) return
lastSelectionRef.current = selection
onBrushSelected?.(selection)
}, [selection, onBrushSelected])
// Consumer-driven clear: when `selections` transitions to 0 (e.g. the user
// presses the clear button in `WidgetSelectionSummary`), drop all drawn
// rectangles and any stored selection.
useEffect(() => {
if (selections !== 0) return
const state = widgetStoreActions.getWidget(id)
const hasRects = (state?.brushRects?.length ?? 0) > 0
const hasSelection = (state?.brushSelection?.dataIndex?.length ?? 0) > 0
if (!hasRects && !hasSelection) return
widgetStoreActions.setWidget(id, {
brushRects: [],
brushSelection: { dataIndex: [], seriesIndex: 0 },
})
}, [selections, id])
const enableLabel = labels?.enable ?? 'Enable brush selection'
const disableLabel = labels?.disable ?? 'Disable brush selection'
const tooltipLabel = brush ? disableLabel : enableLabel
return (
<>
{Icon ?? }
>
)
}