Skip to content

Commit 5090d24

Browse files
committed
feat: RadioGroup, Radio and FieldRadioGroup
1 parent 4988971 commit 5090d24

File tree

3 files changed

+106
-2
lines changed

3 files changed

+106
-2
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { ComponentProps } from 'react';
2+
import {
3+
Controller,
4+
ControllerRenderProps,
5+
FieldPath,
6+
FieldValues,
7+
} from 'react-hook-form';
8+
9+
import { cn } from '@/lib/tailwind/utils';
10+
11+
import { Radio, RadioGroup } from '@/components/ui/radio-group';
12+
13+
import { useFormField } from '../form-field';
14+
import { FieldCommonProps } from '../form-field-controller';
15+
import { FormFieldError } from '../form-field-error';
16+
17+
export type FieldRadioGroupProps<
18+
TFieldValues extends FieldValues = FieldValues,
19+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
20+
> = FieldCommonProps<TFieldValues, TName> & {
21+
type: 'radio-group';
22+
options: Array<{
23+
value: string;
24+
label: string;
25+
disabled?: boolean;
26+
}>;
27+
containerProps?: ComponentProps<'div'>;
28+
} & RemoveFromType<
29+
Omit<
30+
ComponentProps<typeof RadioGroup>,
31+
'id' | 'aria-invalid' | 'aria-describedby'
32+
>,
33+
ControllerRenderProps
34+
>;
35+
36+
export const FieldRadioGroup = <
37+
TFieldValues extends FieldValues = FieldValues,
38+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
39+
>(
40+
props: FieldRadioGroupProps<TFieldValues, TName>
41+
) => {
42+
const {
43+
name,
44+
type,
45+
options,
46+
disabled,
47+
defaultValue,
48+
shouldUnregister,
49+
control,
50+
containerProps,
51+
...rest
52+
} = props;
53+
54+
const ctx = useFormField();
55+
56+
return (
57+
<Controller
58+
name={name}
59+
control={control}
60+
disabled={disabled}
61+
defaultValue={defaultValue}
62+
shouldUnregister={shouldUnregister}
63+
render={({ field: { onChange, ...field }, fieldState }) => (
64+
<div
65+
{...containerProps}
66+
className={cn(
67+
'flex flex-1 flex-col gap-1',
68+
containerProps?.className
69+
)}
70+
>
71+
<RadioGroup
72+
id={ctx.id}
73+
aria-invalid={fieldState.error ? true : undefined}
74+
aria-describedby={
75+
!fieldState.error
76+
? `${ctx.descriptionId}`
77+
: `${ctx.descriptionId} ${ctx.errorId}`
78+
}
79+
{...rest}
80+
onValueChange={onChange}
81+
{...field}
82+
>
83+
{options.map((option) => (
84+
<Radio
85+
key={option.value}
86+
value={option.value}
87+
disabled={option.disabled}
88+
>
89+
{option.label}
90+
</Radio>
91+
))}
92+
</RadioGroup>
93+
<FormFieldError />
94+
</div>
95+
)}
96+
/>
97+
);
98+
};

app/components/form/form-field-controller.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { FieldNumber, FieldNumberProps } from '@/components/form/field-number';
1010

1111
import { FieldDate, FieldDateProps } from './field-date';
1212
import { FieldOtp, FieldOtpProps } from './field-otp';
13+
import { FieldRadioGroup, FieldRadioGroupProps } from './field-radio-group';
1314
import { FieldSelect, FieldSelectProps } from './field-select';
1415
import { FieldText, FieldTextProps } from './field-text';
1516
import { useFormField } from './form-field';
@@ -47,7 +48,8 @@ export type FormFieldControllerProps<
4748
| FieldSelectProps<TFieldValues, TName>
4849
| FieldDateProps<TFieldValues, TName>
4950
| FieldTextProps<TFieldValues, TName>
50-
| FieldOtpProps<TFieldValues, TName>;
51+
| FieldOtpProps<TFieldValues, TName>
52+
| FieldRadioGroupProps<TFieldValues, TName>;
5153

5254
export const FormFieldController = <
5355
TFieldValues extends FieldValues = FieldValues,
@@ -83,6 +85,9 @@ export const FormFieldController = <
8385

8486
case 'number':
8587
return <FieldNumber {...props} />;
88+
89+
case 'radio-group':
90+
return <FieldRadioGroup {...props} />;
8691
// -- ADD NEW FIELD COMPONENT HERE --
8792
}
8893
};

app/components/ui/radio-group.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function Radio({
5050
htmlFor={value}
5151
{...labelProps}
5252
className={cn(
53-
'cursor-pointer peer-disabled:cursor-not-allowed',
53+
'cursor-pointer text-xs peer-disabled:cursor-not-allowed',
5454
labelProps?.className
5555
)}
5656
>
@@ -60,3 +60,4 @@ function Radio({
6060
);
6161
}
6262
export { Radio, RadioGroup };
63+
export type { RadioGroupProps, RadioProps };

0 commit comments

Comments
 (0)