import { Icon } from '@/components/ui/icon';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { CORNERS, FONT_SIZE, HEIGHT } from '@/theme/globals';
import { LucideProps } from 'lucide-react-native';
import React from 'react';
import { TextStyle, TouchableOpacity, ViewStyle } from 'react-native';
type ToggleVariant = 'default' | 'outline';
type ToggleSize = 'default' | 'icon';
interface ToggleProps {
children: React.ReactNode;
pressed?: boolean;
onPressedChange?: (pressed: boolean) => void;
variant?: ToggleVariant;
size?: ToggleSize;
disabled?: boolean;
style?: ViewStyle;
textStyle?: TextStyle;
}
export function Toggle({
children,
pressed = false,
onPressedChange,
variant = 'default',
size = 'icon',
disabled = false,
style,
textStyle,
}: ToggleProps) {
const primaryColor = useColor('primary');
const primaryForegroundColor = useColor('primaryForeground');
const secondaryColor = useColor('secondary');
const secondaryForegroundColor = useColor('secondaryForeground');
const borderColor = useColor('border');
const handlePress = () => {
if (!disabled) {
onPressedChange?.(!pressed);
}
};
const getToggleStyle = (): ViewStyle => {
const baseStyle: ViewStyle = {
borderRadius: CORNERS,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
};
// Size variants - following button component pattern
switch (size) {
case 'icon':
Object.assign(baseStyle, {
width: HEIGHT,
height: HEIGHT,
});
break;
default:
Object.assign(baseStyle, { height: HEIGHT, paddingHorizontal: 32 });
}
// State and variant styles - following button component pattern
if (pressed) {
switch (variant) {
case 'outline':
return {
...baseStyle,
backgroundColor: primaryColor,
borderWidth: 1,
borderColor: primaryColor,
};
default:
return {
...baseStyle,
backgroundColor: primaryColor,
};
}
} else {
switch (variant) {
case 'outline':
return {
...baseStyle,
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: borderColor,
};
default:
return {
...baseStyle,
backgroundColor: secondaryColor,
};
}
}
};
const getToggleTextStyle = (): TextStyle => {
const baseTextStyle: TextStyle = {
fontSize: FONT_SIZE,
fontWeight: '500',
};
if (pressed) {
switch (variant) {
case 'outline':
return { ...baseTextStyle, color: primaryForegroundColor };
default:
return { ...baseTextStyle, color: primaryForegroundColor };
}
} else {
switch (variant) {
case 'outline':
return { ...baseTextStyle, color: primaryColor };
default:
return { ...baseTextStyle, color: secondaryForegroundColor };
}
}
};
const toggleStyle = getToggleStyle();
const finalTextStyle = getToggleTextStyle();
return (
{typeof children === 'string' ? (
{children}
) : (
children
)}
);
}
type ToggleGroupType = 'single' | 'multiple';
type ToggleGroupVariant = 'default' | 'outline';
type ToggleGroupSize = 'default' | 'icon';
interface ToggleGroupItem {
value: string;
label: string;
icon?: React.ComponentType;
disabled?: boolean;
}
interface ToggleGroupProps {
type?: ToggleGroupType;
value?: string | string[];
onValueChange?: (value: string | string[]) => void;
items: ToggleGroupItem[];
variant?: ToggleGroupVariant;
size?: ToggleGroupSize;
disabled?: boolean;
style?: ViewStyle;
orientation?: 'horizontal' | 'vertical';
}
export function ToggleGroup({
type = 'single',
value,
onValueChange,
items,
variant = 'default',
size = 'default',
disabled = false,
style,
orientation = 'horizontal',
}: ToggleGroupProps) {
const borderColor = useColor('border');
const primaryColor = useColor('primary');
const primaryForegroundColor = useColor('primaryForeground');
const secondaryForegroundColor = useColor('secondaryForeground');
const handleItemPress = (itemValue: string) => {
if (disabled) return;
if (type === 'single') {
// Single selection
const newValue = value === itemValue ? undefined : itemValue;
onValueChange?.(newValue || '');
} else {
// Multiple selection
const currentValues = Array.isArray(value) ? value : [];
const newValues = currentValues.includes(itemValue)
? currentValues.filter((v) => v !== itemValue)
: [...currentValues, itemValue];
onValueChange?.(newValues);
}
};
const isItemPressed = (itemValue: string): boolean => {
if (type === 'single') {
return value === itemValue;
} else {
return Array.isArray(value) && value.includes(itemValue);
}
};
const containerStyle: ViewStyle = {
flexDirection: orientation === 'horizontal' ? 'row' : 'column',
borderWidth: 1,
borderColor: borderColor,
borderRadius: CORNERS,
overflow: 'hidden',
backgroundColor: 'transparent',
};
const getItemStyle = (index: number): ViewStyle => {
const isLast = index === items.length - 1;
const itemStyle: ViewStyle = {
flex: orientation === 'horizontal' ? 1 : 0,
borderRadius: 0,
borderWidth: 0,
borderRightWidth: orientation === 'horizontal' && !isLast ? 1 : 0,
borderBottomWidth: orientation === 'vertical' && !isLast ? 1 : 0,
borderColor: borderColor,
};
return itemStyle;
};
return (
{items.map((item, index) => (
handleItemPress(item.value)}
variant={variant}
size={size}
disabled={disabled || item.disabled}
style={getItemStyle(index)}
>
{item.icon && item.label ? (
{item.label}
) : item.icon ? (
) : (
{item.label}
)}
))}
);
}
// Convenience components for common use cases
export function ToggleGroupSingle({
value,
onValueChange,
...props
}: Omit & {
value?: string;
onValueChange?: (value: string) => void;
}) {
const handleValueChange = (newValue: string | string[]) => {
// For single selection, we know it will always be a string
onValueChange?.(newValue as string);
};
return (
);
}
export function ToggleGroupMultiple({
value,
onValueChange,
...props
}: Omit & {
value?: string[];
onValueChange?: (value: string[]) => void;
}) {
const handleValueChange = (newValue: string | string[]) => {
// For multiple selection, we know it will always be a string array
onValueChange?.(newValue as string[]);
};
return (
);
}