You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
memos/web/src/components/MemoMetadata/Relation/LinkMemoDialog.tsx

91 lines
3.4 KiB
TypeScript

import { timestampDate } from "@bufbuild/protobuf/wkt";
import { LinkIcon } from "lucide-react";
import { MemoPreview } from "@/components/MemoPreview";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { VisuallyHidden } from "@/components/ui/visually-hidden";
import { cn } from "@/lib/utils";
import type { Memo } from "@/types/proto/api/v1/memo_service_pb";
import { useTranslate } from "@/utils/i18n";
interface LinkMemoDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
searchText: string;
onSearchChange: (text: string) => void;
filteredMemos: Memo[];
isFetching: boolean;
onSelectMemo: (memo: Memo) => void;
isAlreadyLinked: (memoName: string) => boolean;
}
export const LinkMemoDialog = ({
open,
onOpenChange,
searchText,
onSearchChange,
filteredMemos,
isFetching,
onSelectMemo,
isAlreadyLinked,
}: LinkMemoDialogProps) => {
const t = useTranslate();
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-[min(28rem,calc(100vw-2rem))] p-0!" showCloseButton={false}>
<VisuallyHidden>
<DialogClose />
</VisuallyHidden>
<VisuallyHidden>
<DialogTitle>{t("tooltip.link-memo")}</DialogTitle>
</VisuallyHidden>
<VisuallyHidden>
<DialogDescription>Search and select a memo to link</DialogDescription>
</VisuallyHidden>
<div className="flex flex-col">
<div className="p-3">
<Input
placeholder={t("reference.search-placeholder")}
value={searchText}
onChange={(e) => onSearchChange(e.target.value)}
className="!text-sm h-9"
autoFocus
/>
</div>
<div className="border-t border-border" />
<div className="max-h-[320px] overflow-y-auto">
{filteredMemos.length === 0 ? (
<div className="py-8 text-center text-sm text-muted-foreground">
{isFetching ? "Loading..." : t("reference.no-memos-found")}
</div>
) : (
filteredMemos.map((memo) => {
const alreadyLinked = isAlreadyLinked(memo.name);
return (
<div
key={memo.name}
className={cn(
"flex cursor-pointer items-start border-b border-border last:border-b-0 px-3 py-2.5 hover:bg-accent/50 transition-colors",
alreadyLinked && "opacity-40 cursor-default",
)}
onClick={() => !alreadyLinked && onSelectMemo(memo)}
>
<div className="w-full flex flex-col gap-1">
<div className="flex items-center gap-1.5 text-sm text-muted-foreground select-none">
{alreadyLinked && <LinkIcon className="w-3 h-3 shrink-0" />}
<span>{memo.displayTime && timestampDate(memo.displayTime).toLocaleString()}</span>
</div>
<MemoPreview name={memo.name} content={memo.content} attachments={memo.attachments} showMemoId />
</div>
</div>
);
})
)}
</div>
</div>
</DialogContent>
</Dialog>
);
};