| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | import { MessageCircle, Trash2 } from "lucide-react"; | 1 | import { MessageCircle, Trash2 } from "lucide-react"; |
| 2 | import { useState, type ReactNode } from "react"; | 2 | import { useLayoutEffect, useRef, useState, type ReactNode } from "react"; |
| 3 | | 3 | |
| 4 | import { Button } from "~/components/ui/button"; | 4 | import { Button } from "~/components/ui/button"; |
| 5 | import { Textarea } from "~/components/ui/textarea"; | 5 | import { Textarea } from "~/components/ui/textarea"; |
| ... | @@ -25,6 +25,7 @@ interface DiffCommentAnnotationProps { | ... | @@ -25,6 +25,7 @@ interface DiffCommentAnnotationProps { |
| 25 | submitLabel?: string; | 25 | submitLabel?: string; |
| 26 | pending?: boolean; | 26 | pending?: boolean; |
| 27 | secondaryAction?: DiffCommentSecondaryAction; | 27 | secondaryAction?: DiffCommentSecondaryAction; |
| | 28 | focusOnMount?: boolean; |
| 28 | } | 29 | } |
| 29 | | 30 | |
| 30 | /** The shared inline comment treatment for file previews, thread diffs, and pull-request diffs. */ | 31 | /** The shared inline comment treatment for file previews, thread diffs, and pull-request diffs. */ |
| ... | @@ -40,10 +41,20 @@ export function DiffCommentAnnotation({ | ... | @@ -40,10 +41,20 @@ export function DiffCommentAnnotation({ |
| 40 | submitLabel = "Comment", | 41 | submitLabel = "Comment", |
| 41 | pending = false, | 42 | pending = false, |
| 42 | secondaryAction, | 43 | secondaryAction, |
| | 44 | focusOnMount = true, |
| 43 | }: DiffCommentAnnotationProps) { | 45 | }: DiffCommentAnnotationProps) { |
| 44 | const [localDraftText, setLocalDraftText] = useState(""); | 46 | const [localDraftText, setLocalDraftText] = useState(""); |
| 45 | const displayedText = kind === "draft" && !onTextChange ? localDraftText : text; | 47 | const displayedText = kind === "draft" && !onTextChange ? localDraftText : text; |
| 46 | const trimmedText = displayedText.trim(); | 48 | const trimmedText = displayedText.trim(); |
| | 49 | const textareaRef = useRef<HTMLTextAreaElement | null>(null); |
| | 50 | |
| | 51 | useLayoutEffect(() => { |
| | 52 | if (kind !== "draft" || !focusOnMount) return; |
| | 53 | const frame = window.requestAnimationFrame(() => { |
| | 54 | textareaRef.current?.focus({ preventScroll: true }); |
| | 55 | }); |
| | 56 | return () => window.cancelAnimationFrame(frame); |
| | 57 | }, [focusOnMount, kind]); |
| 47 | | 58 | |
| 48 | if (kind === "comment") { | 59 | if (kind === "comment") { |
| 49 | return ( | 60 | return ( |
| ... | @@ -78,9 +89,10 @@ export function DiffCommentAnnotation({ | ... | @@ -78,9 +89,10 @@ export function DiffCommentAnnotation({ |
| 78 | onPointerDown={(event) => event.stopPropagation()} | 89 | onPointerDown={(event) => event.stopPropagation()} |
| 79 | > | 90 | > |
| 80 | <Textarea | 91 | <Textarea |
| 81 | autoFocus | 92 | ref={textareaRef} |
| | 93 | autoFocus={focusOnMount} |
| 82 | unstyled | 94 | unstyled |
| 83 | className="relative inline-flex w-full rounded-md border border-border/50 bg-background/20 font-sans text-foreground transition-colors focus-within:border-border/70 [&_[data-slot=textarea]]:min-h-12 [&_[data-slot=textarea]]:cursor-text [&_[data-slot=textarea]]:px-2.5 [&_[data-slot=textarea]]:py-1.5 [&_[data-slot=textarea]]:font-sans [&_[data-slot=textarea]]:text-xs [&_[data-slot=textarea]]:leading-5 max-sm:[&_[data-slot=textarea]]:min-h-12" | 95 | className="relative inline-flex w-full rounded-md border border-border/50 bg-background/20 font-sans text-foreground transition-colors focus-within:border-border/70 [&_[data-slot=textarea]]:min-h-12 [&_[data-slot=textarea]]:cursor-text [&_[data-slot=textarea]]:caret-foreground [&_[data-slot=textarea]]:px-2.5 [&_[data-slot=textarea]]:py-1.5 [&_[data-slot=textarea]]:font-sans [&_[data-slot=textarea]]:text-xs [&_[data-slot=textarea]]:leading-5 max-sm:[&_[data-slot=textarea]]:min-h-12" |
| 84 | size="sm" | 96 | size="sm" |
| 85 | value={displayedText} | 97 | value={displayedText} |
| 86 | placeholder={placeholder} | 98 | placeholder={placeholder} |