handy online tools for AT Protocol
boat.kelinci.net
atproto
bluesky
atcute
typescript
solidjs
1import { createEffect, type JSX } from 'solid-js';
2
3import { createId } from '~/lib/hooks/id';
4
5import type { BoundInputEvent } from './_types';
6
7export interface TextInputProps {
8 label: JSX.Element;
9 blurb?: JSX.Element;
10 monospace?: boolean;
11 type?: 'text' | 'password' | 'url' | 'email';
12 name?: string;
13 required?: boolean;
14 disabled?: boolean;
15 autocomplete?: 'off' | 'on' | 'one-time-code' | 'username';
16 autocorrect?: 'off' | 'on';
17 pattern?: string;
18 placeholder?: string;
19 value?: string;
20 autofocus?: boolean;
21 onChange?: (next: string, event: BoundInputEvent<HTMLInputElement>) => void;
22}
23
24const textInputStyles = ({ monospace = false }: TextInputProps) => {
25 let cn = `rounded border border-gray-400 px-3 py-2 text-sm placeholder:text-gray-400 focus:border-purple-800 focus:ring-1 focus:ring-purple-800 focus:ring-offset-0`;
26
27 if (monospace) {
28 cn += ` font-mono tracking-wide`;
29 }
30
31 return cn;
32};
33
34const TextInput = (props: TextInputProps) => {
35 const fieldId = createId();
36
37 const onChange = props.onChange;
38
39 return (
40 <div class="flex flex-col gap-2">
41 <label for={fieldId} class="font-semibold text-gray-600">
42 {props.label}
43 </label>
44
45 <input
46 ref={(node) => {
47 if ('autofocus' in props) {
48 createEffect(() => {
49 if (props.autofocus) {
50 node.focus();
51 }
52 });
53 }
54 }}
55 type={props.type ?? 'text'}
56 id={fieldId}
57 name={props.name}
58 required={props.required}
59 disabled={props.disabled}
60 autocomplete={props.autocomplete}
61 pattern={props.pattern}
62 placeholder={props.placeholder}
63 value={props.value ?? ''}
64 class={textInputStyles(props)}
65 onInput={(event) => onChange?.(event.target.value, event)}
66 />
67
68 <p class="text-pretty text-[0.8125rem] leading-5 text-gray-500 empty:hidden">{props.blurb}</p>
69 </div>
70 );
71};
72
73export default TextInput;