handy online tools for AT Protocol
boat.kelinci.net
atproto
bluesky
atcute
typescript
solidjs
1import type { JSX } from 'solid-js';
2
3import { createId } from '~/lib/hooks/id';
4
5import type { BoundInputEvent } from './_types';
6
7interface RadioInputProps<T extends string> {
8 label: JSX.Element;
9 blurb?: JSX.Element;
10 name?: string;
11 required?: boolean;
12 value?: T;
13 options: { value: NoInfer<T>; label: string; disabled?: boolean }[];
14 onChange?: (next: NoInfer<T>, event: BoundInputEvent<HTMLInputElement>) => void;
15}
16
17const RadioInput = <T extends string>(props: RadioInputProps<T>) => {
18 const fieldId = createId();
19
20 const onChange = props.onChange;
21 const hasValue = 'value' in props;
22
23 return (
24 <fieldset class="flex flex-col gap-2">
25 <legend class="contents">
26 <span class="font-semibold text-gray-600">{props.label}</span>
27 </legend>
28
29 {props.options.map(({ value, label, disabled }, idx) => {
30 const optionId = fieldId + idx;
31
32 return (
33 <fieldset disabled={disabled} class="flex items-center gap-3 disabled:opacity-50">
34 <input
35 type="radio"
36 id={optionId}
37 name={props.name ?? fieldId}
38 required={props.required}
39 value={value}
40 checked={hasValue ? props.value === value : false}
41 class="border-gray-400 text-purple-800 focus:ring-purple-800"
42 onInput={(event) => onChange?.(value, event)}
43 />
44
45 <label for={optionId} class="text-sm">
46 {label}
47 </label>
48 </fieldset>
49 );
50 })}
51
52 <p class="text-pretty text-[0.8125rem] leading-5 text-gray-500 empty:hidden">{props.blurb}</p>
53 </fieldset>
54 );
55};
56
57export default RadioInput;