Files

108 lines
2.9 KiB
TypeScript
Raw Permalink Normal View History

2026-02-05 13:16:05 +08:00
import { useEffect, useRef } from "react"
import { StyleProp, View, ViewStyle, Animated } from "react-native"
import { useAppTheme } from "@/theme/context"
import { $styles } from "@/theme/styles"
import { s } from "@/utils/responsive"
import { $inputOuterBase, BaseToggleInputProps, ToggleProps, Toggle } from "./Toggle"
export interface RadioToggleProps extends Omit<ToggleProps<RadioInputProps>, "ToggleInput"> {
/**
* Optional style prop that affects the dot View.
*/
inputDetailStyle?: ViewStyle
}
interface RadioInputProps extends BaseToggleInputProps<RadioToggleProps> {}
/**
* @param {RadioToggleProps} props - The props for the `Radio` component.
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Radio}
* @returns {JSX.Element} The rendered `Radio` component.
*/
export function Radio(props: RadioToggleProps) {
return <Toggle accessibilityRole="radio" {...props} ToggleInput={RadioInput} />
}
function RadioInput(props: RadioInputProps) {
const {
on,
status,
disabled,
outerStyle: $outerStyleOverride,
innerStyle: $innerStyleOverride,
detailStyle: $detailStyleOverride,
} = props
const {
theme: { colors },
} = useAppTheme()
const opacity = useRef(new Animated.Value(0))
useEffect(() => {
Animated.timing(opacity.current, {
toValue: on ? 1 : 0,
duration: 300,
useNativeDriver: true,
}).start()
}, [on])
const offBackgroundColor = [
disabled && colors.palette.neutral400,
status === "error" && colors.errorBackground,
colors.palette.neutral200,
].filter(Boolean)[0]
const outerBorderColor = [
disabled && colors.palette.neutral400,
status === "error" && colors.error,
!on && colors.palette.neutral800,
colors.palette.secondary500,
].filter(Boolean)[0]
const onBackgroundColor = [
disabled && colors.transparent,
status === "error" && colors.errorBackground,
colors.palette.neutral100,
].filter(Boolean)[0]
const dotBackgroundColor = [
disabled && colors.palette.neutral600,
status === "error" && colors.error,
colors.palette.secondary500,
].filter(Boolean)[0]
return (
<View
style={[
$inputOuter,
{ backgroundColor: offBackgroundColor, borderColor: outerBorderColor },
$outerStyleOverride,
]}
>
<Animated.View
style={[
$styles.toggleInner,
{ backgroundColor: onBackgroundColor },
$innerStyleOverride,
{ opacity: opacity.current },
]}
>
<View
style={[$radioDetail, { backgroundColor: dotBackgroundColor }, $detailStyleOverride]}
/>
</Animated.View>
</View>
)
}
const $radioDetail: ViewStyle = {
width: s(12),
height: s(12),
borderRadius: s(6),
}
const $inputOuter: StyleProp<ViewStyle> = [$inputOuterBase, { borderRadius: s(12) }]