mirror of https://github.com/usememos/memos.git
163 lines
5.4 KiB
TypeScript
163 lines
5.4 KiB
TypeScript
import {
|
|
ArchiveIcon,
|
|
ArchiveRestoreIcon,
|
|
BookmarkMinusIcon,
|
|
BookmarkPlusIcon,
|
|
CopyIcon,
|
|
Edit3Icon,
|
|
FileTextIcon,
|
|
LinkIcon,
|
|
MoreVerticalIcon,
|
|
SquareCheckIcon,
|
|
TrashIcon,
|
|
} from "lucide-react";
|
|
import { observer } from "mobx-react-lite";
|
|
import { useState } from "react";
|
|
import ConfirmDialog from "@/components/ConfirmDialog";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuSub,
|
|
DropdownMenuSubContent,
|
|
DropdownMenuSubTrigger,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { State } from "@/types/proto/api/v1/common_pb";
|
|
import { useTranslate } from "@/utils/i18n";
|
|
import { hasCompletedTasks } from "@/utils/markdown-manipulation";
|
|
import { useMemoActionHandlers } from "./hooks";
|
|
import type { MemoActionMenuProps } from "./types";
|
|
|
|
const MemoActionMenu = observer((props: MemoActionMenuProps) => {
|
|
const { memo, readonly } = props;
|
|
const t = useTranslate();
|
|
|
|
// Dialog state
|
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
const [removeTasksDialogOpen, setRemoveTasksDialogOpen] = useState(false);
|
|
|
|
// Derived state
|
|
const hasCompletedTaskList = hasCompletedTasks(memo.content);
|
|
const isComment = Boolean(memo.parent);
|
|
const isArchived = memo.state === State.ARCHIVED;
|
|
|
|
// Action handlers
|
|
const {
|
|
handleTogglePinMemoBtnClick,
|
|
handleEditMemoClick,
|
|
handleToggleMemoStatusClick,
|
|
handleCopyLink,
|
|
handleCopyContent,
|
|
handleDeleteMemoClick,
|
|
confirmDeleteMemo,
|
|
handleRemoveCompletedTaskListItemsClick,
|
|
confirmRemoveCompletedTaskListItems,
|
|
} = useMemoActionHandlers({
|
|
memo,
|
|
onEdit: props.onEdit,
|
|
setDeleteDialogOpen,
|
|
setRemoveTasksDialogOpen,
|
|
});
|
|
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" size="icon" className="size-4">
|
|
<MoreVerticalIcon className="text-muted-foreground" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" sideOffset={2}>
|
|
{/* Edit actions (non-readonly, non-archived) */}
|
|
{!readonly && !isArchived && (
|
|
<>
|
|
{!isComment && (
|
|
<DropdownMenuItem onClick={handleTogglePinMemoBtnClick}>
|
|
{memo.pinned ? <BookmarkMinusIcon className="w-4 h-auto" /> : <BookmarkPlusIcon className="w-4 h-auto" />}
|
|
{memo.pinned ? t("common.unpin") : t("common.pin")}
|
|
</DropdownMenuItem>
|
|
)}
|
|
<DropdownMenuItem onClick={handleEditMemoClick}>
|
|
<Edit3Icon className="w-4 h-auto" />
|
|
{t("common.edit")}
|
|
</DropdownMenuItem>
|
|
</>
|
|
)}
|
|
|
|
{/* Copy submenu (non-archived) */}
|
|
{!isArchived && (
|
|
<DropdownMenuSub>
|
|
<DropdownMenuSubTrigger>
|
|
<CopyIcon className="w-4 h-auto" />
|
|
{t("common.copy")}
|
|
</DropdownMenuSubTrigger>
|
|
<DropdownMenuSubContent>
|
|
<DropdownMenuItem onClick={handleCopyLink}>
|
|
<LinkIcon className="w-4 h-auto" />
|
|
{t("memo.copy-link")}
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={handleCopyContent}>
|
|
<FileTextIcon className="w-4 h-auto" />
|
|
{t("memo.copy-content")}
|
|
</DropdownMenuItem>
|
|
</DropdownMenuSubContent>
|
|
</DropdownMenuSub>
|
|
)}
|
|
|
|
{/* Write actions (non-readonly) */}
|
|
{!readonly && (
|
|
<>
|
|
{/* Remove completed tasks (non-archived, non-comment, has completed tasks) */}
|
|
{!isArchived && !isComment && hasCompletedTaskList && (
|
|
<DropdownMenuItem onClick={handleRemoveCompletedTaskListItemsClick}>
|
|
<SquareCheckIcon className="w-4 h-auto" />
|
|
{t("memo.remove-completed-task-list-items")}
|
|
</DropdownMenuItem>
|
|
)}
|
|
|
|
{/* Archive/Restore (non-comment) */}
|
|
{!isComment && (
|
|
<DropdownMenuItem onClick={handleToggleMemoStatusClick}>
|
|
{isArchived ? <ArchiveRestoreIcon className="w-4 h-auto" /> : <ArchiveIcon className="w-4 h-auto" />}
|
|
{isArchived ? t("common.restore") : t("common.archive")}
|
|
</DropdownMenuItem>
|
|
)}
|
|
|
|
{/* Delete */}
|
|
<DropdownMenuItem onClick={handleDeleteMemoClick}>
|
|
<TrashIcon className="w-4 h-auto" />
|
|
{t("common.delete")}
|
|
</DropdownMenuItem>
|
|
</>
|
|
)}
|
|
</DropdownMenuContent>
|
|
|
|
{/* Delete confirmation dialog */}
|
|
<ConfirmDialog
|
|
open={deleteDialogOpen}
|
|
onOpenChange={setDeleteDialogOpen}
|
|
title={t("memo.delete-confirm")}
|
|
confirmLabel={t("common.delete")}
|
|
description={t("memo.delete-confirm-description")}
|
|
cancelLabel={t("common.cancel")}
|
|
onConfirm={confirmDeleteMemo}
|
|
confirmVariant="destructive"
|
|
/>
|
|
|
|
{/* Remove completed tasks confirmation */}
|
|
<ConfirmDialog
|
|
open={removeTasksDialogOpen}
|
|
onOpenChange={setRemoveTasksDialogOpen}
|
|
title={t("memo.remove-completed-task-list-items-confirm")}
|
|
confirmLabel={t("common.confirm")}
|
|
cancelLabel={t("common.cancel")}
|
|
onConfirm={confirmRemoveCompletedTaskListItems}
|
|
confirmVariant="destructive"
|
|
/>
|
|
</DropdownMenu>
|
|
);
|
|
});
|
|
|
|
export default MemoActionMenu;
|