103 lines · 3.6 KB · TypeScript
RawDownload
import { MessageCircle, Trash2 } from "lucide-react";import { useState } from "react"; import { Button } from "~/components/ui/button";import { Textarea } from "~/components/ui/textarea"; interface LocalCommentAnnotationProps {  kind: "draft" | "comment";  rangeLabel: string;  text: string;  onTextChange?: (text: string) => void;  onCancel: () => void;  onComment: (text: string) => void;  onDelete: () => void;} export function LocalCommentAnnotation({  kind,  rangeLabel,  text,  onTextChange,  onCancel,  onComment,  onDelete,}: LocalCommentAnnotationProps) {  const [localDraftText, setLocalDraftText] = useState("");  const displayedText = kind === "draft" && !onTextChange ? localDraftText : text;   if (kind === "comment") {    return (      <div        data-file-comment-annotation        className="group/comment flex min-w-0 items-start gap-2.5 border-s-2 border-primary/55 bg-primary/[0.045] px-3 py-2.5 font-sans text-foreground"        contentEditable={false}        onPointerDown={(event) => event.stopPropagation()}      >        <MessageCircle className="mt-0.5 size-3.5 shrink-0 text-primary/70" aria-hidden="true" />        <p className="min-w-0 flex-1 whitespace-pre-wrap text-[13px] leading-5">{displayedText}</p>        <Button          className="-my-1 -mr-1 shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover/comment:opacity-100 focus-visible:opacity-100 max-sm:opacity-100"          variant="ghost"          size="icon-xs"          aria-label="Delete comment"          onClick={onDelete}        >          <Trash2 className="size-3" />        </Button>      </div>    );  }   return (    <div      data-file-comment-annotation      className="px-3 py-2 font-sans text-foreground"      contentEditable={false}      onPointerDown={(event) => event.stopPropagation()}    >      <Textarea        autoFocus        unstyled        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"        size="sm"        value={displayedText}        placeholder="Add a comment…"        aria-label={`Comment on lines ${rangeLabel}`}        onChange={(event) => (onTextChange ?? setLocalDraftText)(event.target.value)}        onFocus={(event) => {          const end = event.currentTarget.value.length;          event.currentTarget.setSelectionRange(end, end);        }}        onKeyDown={(event) => {          if (event.key === "Escape") {            event.preventDefault();            onCancel();          }          if ((event.metaKey || event.ctrlKey) && event.key === "Enter" && displayedText.trim()) {            event.preventDefault();            onComment(displayedText.trim());          }        }}      />      <div className="mt-1.5 flex items-center gap-1">        <span className="mr-auto text-[10px] text-muted-foreground/70">⌘/Ctrl Enter to send</span>        <Button          className="text-muted-foreground hover:text-foreground"          variant="ghost"          size="xs"          onClick={onCancel}        >          Cancel        </Button>        <Button          size="xs"          disabled={!displayedText.trim()}          onClick={() => onComment(displayedText.trim())}        >          Comment        </Button>      </div>    </div>  );}