/** * Copyright (c) Paymium. * * This source code is licensed under the MIT license found in the * LICENSE file in the root of this projects source tree. */ import { createScope, useUncontrolled, withStaticProperties, } from '@crossed/core'; import { composeStyles, createStyles, CrossedMethods } from '@crossed/styled'; import { forwardRef, memo, RefAttributes, useCallback, useTransition, type PropsWithChildren, } from 'react'; import { Pressable, PressableProps, View, ViewProps } from 'react-native'; import { Text } from '../../typography/Text'; const rootStyles = createStyles((t) => ({ default: { base: { width: 16, height: 16, borderRadius: 44, borderWidth: 1, borderColor: t.colors.border.secondary, alignItems: 'center', display: 'flex', justifyContent: 'center', backgroundColor: t.colors.background.secondary, }, }, hover: { base: { borderColor: t.colors.border.tertiary } }, active: { base: { borderColor: t.colors.border.tertiary }, web: { base: { transition: 'all 0.1s ease', boxShadow: `0px 0px 0px 2px ${t.colors.border.secondary}`, }, }, }, disabled: { base: { backgroundColor: t.colors.primary['10'], borderColor: t.colors.primary['10'], }, }, checked: { base: { borderColor: t.colors.primary.primary } }, checkedActive: { base: { borderColor: t.colors.primary.primary } }, })); const pressableStyles = createStyles(({ space }) => ({ pressable: { base: { alignItems: 'center', display: 'flex', flexDirection: 'row', gap: space.xl, }, }, })); const thumbStyles = createStyles((t) => ({ default: { base: { width: 10, height: 10, borderRadius: 10, borderWidth: 1, borderStyle: 'solid', borderColor: 'transparent', backgroundColor: 'transparent', }, }, checked: { base: { backgroundColor: t.colors.primary.primary, borderColor: t.colors.primary.primary, }, }, checkedActive: { base: { backgroundColor: t.colors.primary['1'], borderColor: t.colors.primary['1'], }, }, checkedDisabled: { base: { backgroundColor: t.colors.neutral['70'], borderColor: t.colors.neutral['70'], }, }, })); type RadioContext = { checked: boolean }; const [RadioProvider, useRadioContext] = createScope({ checked: false, }); type RadioStateInteraction = { active: boolean; hover: boolean; disabled: boolean; }; const [RadioInteractionProvider, useStateInteraction] = createScope({ active: false, hover: false, disabled: false, }); type RadioThumbProps = Omit & { style?: CrossedMethods; }; const RadioThumb = memo>( forwardRef((props, ref) => { const { active, hover, disabled } = useStateInteraction(); const { checked } = useRadioContext(); return ( ); }) ); type RadioRootProps = Omit & PropsWithChildren<{ /** * if true, radio is checked */ checked?: boolean; /** * default checked value */ defaultChecked?: boolean; /** * Call when checked value change */ onChecked?: (_c: boolean) => void; disabled?: boolean; noThumb?: boolean; style?: CrossedMethods; }>; export const RadioRoot = ({ children, disabled, checked: checkedProps, defaultChecked = false, onChecked, noThumb, style, ...props }: RadioRootProps) => { const [checked, setChecked] = useUncontrolled({ defaultValue: defaultChecked, value: checkedProps, onChange: onChecked, }); const [, setTransition] = useTransition(); const handlePress = useCallback( (e) => { props.onPress?.(e); setTransition(() => { setChecked(!checked); }); }, [setChecked, checked, setTransition, props.onPress] ); return ( composeStyles(pressableStyles.pressable, style).rnw({ active: pressed, hover: hovered, focus: focused, }).style } > {({ hovered, pressed }: { hovered?: boolean; pressed: boolean }) => ( {!noThumb && } {typeof children === 'string' ? {children} : children} )} ); }; RadioRoot.displayName = 'Radio'; export const Radio = withStaticProperties(RadioRoot, { Thumb: RadioThumb });