import React, { createContext, useContext } from 'react'; import { useStyles } from '../../core/hooks/useStyles'; import { useTheme } from '../../core/theme/ThemeProvider'; const ToggleButtonDivider: React.FC = () => { const { theme } = useTheme(); const createStyle = useStyles('toggle-button-divider'); const dividerClass = createStyle({ width: '1px', backgroundColor: theme.colors.border, margin: '0.5rem 0', }); return
; }; interface ToggleButtonGroupContextType { value: string | string[] | null; onChange: (value: string | string[] | null) => void; type: 'single' | 'multiple'; hasCustomDividers: boolean; } const ToggleButtonGroupContext = createContext(null); export const useToggleButtonGroup = () => { const context = useContext(ToggleButtonGroupContext); if (!context) { throw new Error('ToggleButton must be used within a ToggleButtonGroup.'); } return context; }; interface ToggleButtonGroupProps { children: React.ReactNode; value: string | string[] | null; onChange: (value: string | string[] | null) => void; type?: 'single' | 'multiple'; className?: string; maxSplit?: number; maxWidth?: string; } interface ToggleButtonGroupFC extends React.FC { Divider: React.FC; } export const ToggleButtonGroup: ToggleButtonGroupFC = ({ children, value, onChange, type = 'single', className, maxSplit, maxWidth }) => { const { theme } = useTheme(); const createStyle = useStyles('toggle-button-group'); const childrenArray = React.Children.toArray(children); const isSplitByMax = maxSplit && maxSplit > 0; const hasCustomDividers = childrenArray.some( (child) => React.isValidElement(child) && child.type === ToggleButtonDivider ); let content: React.ReactNode; if (isSplitByMax) { // If using maxSplit, we create distinct visual rows. const rows: React.ReactNode[][] = []; const buttonsOnly = childrenArray.filter( (c) => React.isValidElement(c) && c.type !== ToggleButtonDivider ); for (let i = 0; i < buttonsOnly.length; i += maxSplit!) { rows.push(buttonsOnly.slice(i, i + maxSplit!)); } const rowContainerClass = createStyle({ display: 'inline-flex', borderRadius: '8px', overflow: 'hidden', border: `1px solid ${theme.colors.border}`, }); content = rows.map((row, index) => (
{row}
)); } else { content = childrenArray; } const containerClass = createStyle({ display: isSplitByMax ? 'flex' : 'inline-flex', flexDirection: isSplitByMax ? 'column' : 'row', alignItems: isSplitByMax ? 'center' : 'stretch', gap: isSplitByMax ? '8px' : '0', flexWrap: 'wrap', // for maxWidth behavior // If not splitting by maxSplit, apply the single-group container styles borderRadius: !isSplitByMax ? '8px' : '0', overflow: !isSplitByMax ? 'hidden' : 'visible', border: !isSplitByMax ? `1px solid ${theme.colors.border}` : 'none', maxWidth: maxWidth, }); const contextValue = { value, onChange, type, hasCustomDividers }; return (
{content}
); }; ToggleButtonGroup.Divider = ToggleButtonDivider; interface ToggleButtonProps extends React.ButtonHTMLAttributes { value: string; } export const ToggleButton: React.FC = ({ children, value, ...props }) => { const { value: groupValue, onChange, type, hasCustomDividers } = useToggleButtonGroup(); const { theme } = useTheme(); const createStyle = useStyles('toggle-button'); const isActive = type === 'multiple' ? (groupValue as string[])?.includes(value) : groupValue === value; const buttonClass = createStyle({ padding: '0.5rem 1rem', border: 'none', borderRight: hasCustomDividers ? 'none' : `1px solid ${theme.colors.border}`, backgroundColor: isActive ? theme.colors.primary : 'transparent', color: isActive ? '#fff' : theme.colors.textSecondary, cursor: 'pointer', transition: 'all 0.2s', '&:last-child': { borderRight: 'none', }, '&:hover': { backgroundColor: isActive ? theme.colors.primary : 'rgba(255, 255, 255, 0.05)', } }); const handleClick = () => { if (type === 'single') { onChange(value); } else { const currentValue = (groupValue as string[] | null) || []; const newValues = currentValue.includes(value) ? currentValue.filter(v => v !== value) : [...currentValue, value]; onChange(newValues); } }; return ( ); };