handy online tools for AT Protocol boat.kelinci.net
atproto bluesky atcute typescript solidjs
at trunk 57 lines 1.5 kB view raw
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;