import { IconButton } from '@mui/material' import { useCallback, useEffect, useMemo } from 'react' import { widgetStoreActions } from '../../stores/widget-store' import type { StackToggleProps, StackToggleState } from './types' import { actionButtonStyles } from '../shared/styles' import { Tooltip } from '../../../components' import { GroupedBarChartIcon } from './grouped-bar-chart-icon' import { getEChartStackConfig } from '../../echart/utils' import { DEFAULT_STACK_GROUP } from '../../echart/const' import type { EchartWidgetState } from '../../echart/types' import type { EchartOptionsProps } from '../../echart/types' import { useWidgetSelector } from '../../stores/use-widget-selector' export const STACK_TOGGLE_TOOL_ID = 'stack-toggle' /** * Widget action to toggle stacking behavior in ECharts bar and histogram widgets. * * Registers as a config pipeline tool so that stack configuration is automatically * re-applied when the base config is updated (e.g., by WidgetLoader). * * @example * ```tsx * * ``` */ export function StackToggle({ id, defaultIsStacked = false, labels, Icon, IconButtonProps, }: StackToggleProps) { const { storeIsStacked, option } = useWidgetSelector(id, (w) => ({ storeIsStacked: (w as StackToggleState | undefined)?.isStacked, option: (w as EchartWidgetState | undefined)?.option, })) const { hasMultiSeries, hasStackInSeries } = useMemo(() => { if (!option) return { hasMultiSeries: false, hasStackInSeries: false } const series = Array.isArray(option.series) ? option.series : [option.series] return { hasMultiSeries: series.length > 1, hasStackInSeries: series.some((s) => (s as { stack?: string })?.stack), } }, [option]) // If series already has stack defined, default to stacked=true const effectiveDefaultIsStacked = hasStackInSeries || defaultIsStacked const isStacked = storeIsStacked ?? effectiveDefaultIsStacked // Register config tool once — fn has no closure deps useEffect(() => { widgetStoreActions.registerTool(id, { id: STACK_TOGGLE_TOOL_ID, type: 'config', order: 10, enabled: false, fn: (currentConfig: unknown) => { const config = currentConfig as Record const option = config.option as EchartOptionsProps | undefined if (!option) return currentConfig const series = Array.isArray(option.series) ? option.series : [option.series] const updatedSeries = series.map((s) => { const existingStack = (s as { stack?: string })?.stack const stackGroup = typeof existingStack === 'string' ? existingStack : DEFAULT_STACK_GROUP return { ...s, ...getEChartStackConfig(stackGroup) } }) return { ...config, option: { ...option, series: updatedSeries } } }, }) return () => widgetStoreActions.unregisterTool(id, STACK_TOGGLE_TOOL_ID) }, [id]) // Sync enabled from store — lightweight, no re-registration useEffect(() => { widgetStoreActions.setToolEnabled( id, STACK_TOGGLE_TOOL_ID, isStacked && hasMultiSeries, ) }, [id, isStacked, hasMultiSeries]) // Initialize store with default value only if not already configured useEffect(() => { if (storeIsStacked !== undefined) return widgetStoreActions.setWidget(id, { isStacked: effectiveDefaultIsStacked }) }, [effectiveDefaultIsStacked, id, storeIsStacked]) const handleToggle = useCallback(() => { widgetStoreActions.setWidget(id, { isStacked: !isStacked }) }, [isStacked, id]) const tooltipLabel = isStacked ? (labels?.unstacked ?? 'Disable stacking') : (labels?.stacked ?? 'Enable stacking') // Early return if there's only one series - stacking doesn't apply if (!hasMultiSeries) { return null } return ( {Icon ?? } ) }