personal web client for Bluesky
typescript
solidjs
bluesky
atcute
1import { type JSX } from 'solid-js';
2
3import { createId } from '~/lib/hooks/id';
4import { useTextareaAutosize } from '~/lib/hooks/textarea-autosize';
5
6import { useFieldset } from './fieldset';
7
8export interface TextareaInputProps {
9 ref?: (node: HTMLTextAreaElement) => void;
10 label?: string;
11 required?: boolean;
12 disabled?: boolean;
13 placeholder?: string;
14 error?: string | null | undefined | false;
15 value?: string;
16 headerAccessory?: JSX.Element;
17 onInput?: (ev: InputEvent) => void;
18 maxRows?: number;
19 minRows?: number;
20}
21
22const TextareaInput = (props: TextareaInputProps) => {
23 const fieldset = useFieldset();
24 const id = createId();
25
26 const hasValue = 'value' in props;
27 const isDisabled = () => fieldset.disabled || !!props.disabled;
28
29 const hasLabel = 'label' in props;
30 const hasHeaderAccessory = 'headerAccessory' in props;
31
32 return (
33 <div class={`flex flex-col gap-2` + (isDisabled() ? ` opacity-50` : ``)}>
34 {(hasLabel || hasHeaderAccessory) && (
35 <div class="flex justify-between gap-2">
36 <label for={id} class="overflow-hidden break-words text-sm font-medium text-contrast">
37 {props.label}
38 </label>
39
40 {props.headerAccessory}
41 </div>
42 )}
43
44 <textarea
45 ref={(node) => {
46 props.ref?.(node);
47
48 const getter = hasValue ? () => props.value : undefined;
49 useTextareaAutosize(node, getter, {
50 maxRows: props.maxRows,
51 minRows: props.minRows,
52 });
53 }}
54 id={id}
55 required={props.required}
56 disabled={isDisabled()}
57 value={hasValue ? props.value : ''}
58 onInput={props.onInput}
59 placeholder={props.placeholder}
60 class="resize-none 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"
61 />
62
63 {props.error && <p class="text-de text-error">{props.error}</p>}
64 </div>
65 );
66};
67
68export default TextareaInput;