82 lines
2.4 KiB
TypeScript
82 lines
2.4 KiB
TypeScript
import { SendHorizonal } from "lucide-react"
|
|
import { useEffect, useRef, useState } from "react"
|
|
|
|
import { cn } from "@/lib/utils"
|
|
|
|
/* ===========================================================================
|
|
Section 24 — the composer.
|
|
Enter sends, Shift+Enter adds a newline, send is disabled when empty, and the
|
|
textarea grows with the content up to a cap.
|
|
=========================================================================== */
|
|
|
|
export function OwliverComposer({
|
|
onSend,
|
|
disabled,
|
|
}: {
|
|
onSend: (text: string) => void
|
|
disabled?: boolean
|
|
}) {
|
|
const [value, setValue] = useState("")
|
|
const ref = useRef<HTMLTextAreaElement>(null)
|
|
|
|
useEffect(() => {
|
|
const el = ref.current
|
|
if (!el) return
|
|
el.style.height = "auto"
|
|
el.style.height = `${Math.min(el.scrollHeight, 120)}px`
|
|
}, [value])
|
|
|
|
const canSend = value.trim().length > 0 && !disabled
|
|
|
|
function submit() {
|
|
if (!canSend) return
|
|
onSend(value.trim())
|
|
setValue("")
|
|
}
|
|
|
|
return (
|
|
<div className="owliver-composer border-t border-line bg-surface p-2.5">
|
|
<div
|
|
className={cn(
|
|
"flex items-end gap-1.5 rounded-[14px] border border-line bg-surface-soft px-2.5 py-1.5",
|
|
"transition-colors focus-within:border-primary focus-within:bg-surface"
|
|
)}
|
|
>
|
|
<textarea
|
|
ref={ref}
|
|
rows={1}
|
|
value={value}
|
|
disabled={disabled}
|
|
onChange={(e) => setValue(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
e.preventDefault()
|
|
submit()
|
|
}
|
|
}}
|
|
placeholder="Ask Owliver anything..."
|
|
aria-label="Ask Owliver anything"
|
|
className={cn(
|
|
"max-h-[120px] min-h-[24px] flex-1 resize-none bg-transparent py-1 text-[13px] leading-snug text-ink",
|
|
"placeholder:text-ink-3 focus:outline-none disabled:opacity-60"
|
|
)}
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={submit}
|
|
disabled={!canSend}
|
|
aria-label="Send message"
|
|
className={cn(
|
|
"mb-0.5 grid size-7 shrink-0 place-items-center rounded-[9px] transition-colors",
|
|
canSend
|
|
? "bg-primary text-white hover:bg-[#1a45c0]"
|
|
: "bg-surface-sunk text-ink-3"
|
|
)}
|
|
>
|
|
<SendHorizonal className="size-3.5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|