Files
llink/js/desktop/src/features/particles/text-reaction-input.tsx
T
2026-06-01 17:08:25 -07:00

104 lines
2.7 KiB
TypeScript

import { useEffect, useRef, useState } from 'react';
import { Send } from 'lucide-react';
import { useSuspendPlayback } from '@/hooks/use-suspend-playback';
import { sanitizeReactionText } from '@/lib/firestore-particles';
import { cn } from '@/lib/utils';
const MAX_LENGTH = 40;
interface TextReactionInputProps {
open: boolean;
onSubmit: (text: string) => void;
onClose: () => void;
}
export function TextReactionInput({
open,
onSubmit,
onClose,
}: TextReactionInputProps) {
const [value, setValue] = useState('');
const [prevOpen, setPrevOpen] = useState(open);
const inputRef = useRef<HTMLInputElement>(null);
useSuspendPlayback(open, 'text-reaction');
if (open !== prevOpen) {
setPrevOpen(open);
// NOTE: perform side effects here when opening
if (open) {
setValue('')
}
}
useEffect(() => {
if (!open) return;
const id = requestAnimationFrame(() => inputRef.current?.focus());
return () => cancelAnimationFrame(id);
}, [open]);
if (!open) return null;
const trimmed = value.trim();
const canSubmit = trimmed.length > 0;
const remaining = MAX_LENGTH - value.length;
const handleSubmit = () => {
if (!canSubmit) return;
onSubmit(trimmed);
setValue('');
onClose();
};
return (
<div
onClick={(e) => e.stopPropagation()}
className="flex items-center gap-1 rounded-full bg-black/60 py-1 pl-3 pr-1 shadow-lg ring-1 ring-white/15 backdrop-blur-md"
>
<input
ref={inputRef}
value={value}
onChange={(e) =>
setValue(sanitizeReactionText(e.target.value).slice(0, MAX_LENGTH))
}
onBlur={onClose}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleSubmit();
} else if (e.key === 'Escape') {
e.preventDefault();
onClose();
}
}}
placeholder="Quick reply…"
maxLength={MAX_LENGTH}
className="w-24 bg-transparent text-sm text-white outline-none placeholder:text-white/40"
/>
<span
className={cn(
'min-w-[1.5ch] text-right text-[10px] tabular-nums',
remaining <= 8 ? 'text-amber-300/80' : 'text-white/30',
)}
>
{remaining}
</span>
<button
onMouseDown={(e) => e.preventDefault()}
onClick={handleSubmit}
disabled={!canSubmit}
className={cn(
'ml-1 flex size-6 items-center justify-center rounded-full transition-colors',
canSubmit
? 'bg-white/20 text-white hover:bg-white/30'
: 'text-white/30',
)}
aria-label="Send reaction"
>
<Send className="size-3" />
</button>
</div>
);
}