48 lines
2.2 KiB
TypeScript
48 lines
2.2 KiB
TypeScript
import { useRef, useState } from "react";
|
|
|
|
import { focusFirstInvalidControl } from "@/shared/lib/focus-first-invalid-control";
|
|
|
|
export function FanTalkReplyForm({ content, errorMessage, isSaving, onChange, onSubmit, submitLabel = "답변 등록" }: { readonly content: string; readonly errorMessage: string | null; readonly isSaving: boolean; readonly onChange: (content: string) => void; readonly onSubmit: () => void; readonly submitLabel?: string }) {
|
|
const errorId = "fan-talk-reply-error";
|
|
const formRef = useRef<HTMLFormElement>(null);
|
|
const [contentErrorMessage, setContentErrorMessage] = useState<string | null>(null);
|
|
const visibleErrorMessage = contentErrorMessage ?? errorMessage;
|
|
|
|
function submit() {
|
|
if (content.trim().length === 0) {
|
|
setContentErrorMessage("답변 내용을 입력해 주세요.");
|
|
queueMicrotask(() => focusFirstInvalidControl(formRef.current));
|
|
return;
|
|
}
|
|
|
|
setContentErrorMessage(null);
|
|
onSubmit();
|
|
}
|
|
|
|
return (
|
|
<form
|
|
className="flex flex-col gap-3"
|
|
onSubmit={(event) => {
|
|
event.preventDefault();
|
|
submit();
|
|
}}
|
|
ref={formRef}
|
|
>
|
|
<label className="flex flex-col gap-2 text-sm font-semibold">
|
|
답변 내용
|
|
<textarea aria-describedby={visibleErrorMessage === null ? undefined : errorId} aria-invalid={visibleErrorMessage === null ? undefined : true} className="min-h-32 rounded-md border border-input bg-card px-3 py-2 text-base font-normal text-foreground" onChange={(event) => {
|
|
const nextContent = event.currentTarget.value;
|
|
if (nextContent.trim().length > 0) {
|
|
setContentErrorMessage(null);
|
|
}
|
|
onChange(nextContent);
|
|
}} value={content} />
|
|
</label>
|
|
{visibleErrorMessage === null ? null : <p aria-label="답변 내용 오류" className="rounded-md border border-destructive bg-card p-3 text-sm font-semibold text-destructive" id={errorId} role="alert">{visibleErrorMessage}</p>}
|
|
<button className="w-fit rounded-md border border-input bg-primary px-4 py-2 font-semibold text-primary-foreground hover:bg-[var(--button-bg-hover)] active:bg-[var(--button-bg-active)] disabled:opacity-60" disabled={isSaving} type="submit">
|
|
{submitLabel}
|
|
</button>
|
|
</form>
|
|
);
|
|
}
|