import React, { useRef, ReactNode } from 'react'; import { TouchableOpacity, Animated, Platform, I18nManager, } from 'react-native'; import { Box } from '../Box'; import type { ResponsiveValue } from '@shopify/restyle'; import { theme, Theme } from '../../theme/theme'; type SwitchButtonProps = { isOn: boolean; onToggle: React.Dispatch>; IconOn?: ReactNode; IconOff?: ReactNode; onColor?: ResponsiveValue; offColor?: ResponsiveValue; size?: 'small' | 'large'; disabled?: boolean; }; const defaultProps = { isOn: true, onColor: theme.colors.green, offColor: theme.colors.gray25, IconOn: {}, IconOff: {}, size: 'medium', labelStyle: {}, thumbOnStyle: {}, thumbOffStyle: {}, trackOnStyle: {}, trackOffStyle: {}, icon: null, disabled: false, animationSpeed: 300, useNativeDriver: true, circleColor: 'white', }; export const SwitchButton = (props: SwitchButtonProps) => { const { isOn, onToggle, disabled, size, onColor, offColor, IconOn, IconOff } = props; const colorOn = onColor || defaultProps.onColor; const colorOff = offColor || defaultProps.offColor; function calculateDimensions(sz: string) { switch (sz) { case 'small': return { width: 40, padding: 10, circleWidth: 15, circleHeight: 15, translateX: 22, }; case 'large': return { width: 52, padding: 16, circleWidth: 28, circleHeight: 28, translateX: 34, }; default: return { width: 48, height: 24, padding: 12, circleWidth: 20, circleHeight: 20, translateX: 26, }; } } const offsetX = useRef(new Animated.Value(0)).current; const dimensions = size ? calculateDimensions(size) : calculateDimensions(defaultProps.size); const createSwitchButtonStyle = () => [ { justifyContent: 'center', width: dimensions.width, height: dimensions.height, borderRadius: 20, padding: dimensions.padding, backgroundColor: isOn ? colorOn : colorOff, }, isOn ? defaultProps.trackOnStyle : defaultProps.trackOffStyle, ]; const createInsideCircleStyle = () => [ { alignItems: 'center', justifyContent: 'center', margin: Platform.OS === 'web' ? 0 : 4, left: Platform.OS === 'web' ? 4 : 0, position: 'absolute', backgroundColor: defaultProps.circleColor, transform: [{ translateX: offsetX }], width: dimensions.circleWidth, height: dimensions.circleHeight, borderRadius: dimensions.circleWidth / 2, shadowColor: '#000', shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.2, shadowRadius: 2.5, elevation: 1.5, }, isOn ? defaultProps.thumbOnStyle : defaultProps.thumbOffStyle, ]; let toValue; if (!I18nManager.isRTL && isOn) { toValue = dimensions.width - dimensions.translateX; } else if (I18nManager.isRTL && isOn) { toValue = -dimensions.width + dimensions.translateX; } else { toValue = -1; } Animated.timing(offsetX, { toValue, duration: defaultProps.animationSpeed, useNativeDriver: true, }).start(); return ( { if (!disabled) { onToggle(!isOn); } }} > {isOn ? IconOn : IconOff} ); };