72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
import type { FormEvent, ReactNode, Ref } from "react";
|
|
import Button from "./Button";
|
|
import InsetPanel from "./InsetPanel";
|
|
import TextInput from "./TextInput";
|
|
import { cx } from "./cx";
|
|
|
|
interface InlineComposerProps {
|
|
label: ReactNode;
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
onSubmit: () => void;
|
|
placeholder?: string;
|
|
hint?: ReactNode;
|
|
error?: ReactNode;
|
|
submitLabel: ReactNode;
|
|
disabled?: boolean;
|
|
inputId: string;
|
|
inputRef?: Ref<HTMLInputElement>;
|
|
className?: string;
|
|
action?: ReactNode;
|
|
}
|
|
|
|
export default function InlineComposer({
|
|
label,
|
|
value,
|
|
onChange,
|
|
onSubmit,
|
|
placeholder,
|
|
hint,
|
|
error,
|
|
submitLabel,
|
|
disabled = false,
|
|
inputId,
|
|
inputRef,
|
|
className,
|
|
action,
|
|
}: InlineComposerProps) {
|
|
return (
|
|
<InsetPanel className={cx("space-y-2.5", className)} padding="sm" as="form" onSubmit={(event: FormEvent) => {
|
|
event.preventDefault();
|
|
onSubmit();
|
|
}}
|
|
>
|
|
<label htmlFor={inputId} className="app-text-soft app-overline mb-1 block">
|
|
{label}
|
|
</label>
|
|
<div className="flex flex-col gap-2 sm:flex-row">
|
|
<TextInput
|
|
ref={inputRef}
|
|
id={inputId}
|
|
value={value}
|
|
onChange={(event) => onChange(event.target.value)}
|
|
placeholder={placeholder}
|
|
disabled={disabled}
|
|
className="flex-1"
|
|
/>
|
|
{action ?? (
|
|
<Button type="submit" disabled={disabled || !value.trim()} size="xs" variant="solid" tone="brand">
|
|
{submitLabel}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
{hint ? <div className="app-text-faint app-caption mt-1">{hint}</div> : null}
|
|
{error ? (
|
|
<div role="alert" className="app-text-danger app-caption mt-2">
|
|
{error}
|
|
</div>
|
|
) : null}
|
|
</InsetPanel>
|
|
);
|
|
}
|