personal web client for Bluesky
typescript solidjs bluesky atcute
at trunk 76 lines 2.0 kB view raw
1import type { JSX } from 'solid-js'; 2 3import { createId } from '~/lib/hooks/id'; 4 5import { useFieldset } from './fieldset'; 6 7export interface TextInputProps { 8 ref?: (node: HTMLInputElement) => void; 9 label?: string; 10 type?: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url'; 11 name?: string; 12 autocomplete?: 13 | 'off' 14 | 'on' 15 | 'name' 16 | 'email' 17 | 'username' 18 | 'current-password' 19 | 'new-password' 20 | 'one-time-code'; 21 pattern?: string; 22 required?: boolean; 23 disabled?: boolean; 24 placeholder?: string; 25 error?: string | null | undefined | false; 26 description?: string; 27 value?: string; 28 headerAccessory?: JSX.Element; 29 onInput?: (ev: InputEvent) => void; 30} 31 32const TextInput = (props: TextInputProps) => { 33 const fieldset = useFieldset(); 34 const id = createId(); 35 36 const hasValue = 'value' in props; 37 const isDisabled = () => fieldset.disabled || !!props.disabled; 38 39 const hasLabel = 'label' in props; 40 const hasHeaderAccessory = 'headerAccessory' in props; 41 42 return ( 43 <div class={`flex flex-col gap-2` + (isDisabled() ? ` opacity-50` : ``)}> 44 {(hasLabel || hasHeaderAccessory) && ( 45 <div class="flex justify-between gap-2"> 46 <label for={id} class="overflow-hidden break-words text-sm font-medium text-contrast"> 47 {props.label} 48 </label> 49 50 {props.headerAccessory} 51 </div> 52 )} 53 54 <input 55 ref={props.ref} 56 id={id} 57 name={props.name} 58 type={props.type || 'text'} 59 autocomplete={props.autocomplete} 60 pattern={props.pattern} 61 required={props.required} 62 disabled={isDisabled()} 63 value={hasValue ? props.value : ''} 64 onInput={props.onInput} 65 placeholder={props.placeholder} 66 class="rounded border border-outline-md bg-background px-3 py-2 text-sm leading-6 text-contrast outline-2 -outline-offset-2 outline-accent placeholder:text-contrast-muted focus:outline" 67 /> 68 69 {props.error && <p class="text-de text-error">{props.error}</p>} 70 71 <p class="text-pretty text-de text-contrast-muted empty:hidden">{props.description}</p> 72 </div> 73 ); 74}; 75 76export default TextInput;