diff --git a/apps/web/src/components/EditorPane.tsx b/apps/web/src/components/EditorPane.tsx index f1b91969..d61e2038 100644 --- a/apps/web/src/components/EditorPane.tsx +++ b/apps/web/src/components/EditorPane.tsx @@ -167,17 +167,37 @@ type NoteLinkHintPosition = { }; type AttachmentMenuTarget = { - href: string; + kind: "attachment"; + url: string; filename: string; resourceId: string | null; position: NoteLinkHintPosition; }; -type AttachmentDialogState = { - action: "rename" | "delete"; - target: AttachmentMenuTarget; +type ImageMenuTarget = { + kind: "image"; + url: string; + filename: string; + resourceId: string | null; + position: NoteLinkHintPosition; + updateAttributes: (attributes: Record) => void; + deleteNode: () => void; }; +type ResourceMenuTarget = AttachmentMenuTarget | ImageMenuTarget; + +type ResourceDialogState = { + action: "rename" | "delete"; + target: ResourceMenuTarget; +}; + +type ImageMenuRequestDetail = Omit & { + element: HTMLElement; +}; + +const IMAGE_MENU_SHOW_EVENT = "edgeever:image-menu-show"; +const IMAGE_MENU_HIDE_EVENT = "edgeever:image-menu-hide"; + const getAttachmentLinkFromEventTarget = (target: EventTarget | null) => target instanceof Element ? target.closest( @@ -222,27 +242,31 @@ const NoteLinkInteractionHint = ({ document.body ); -const AttachmentActionMenu = ({ +const ResourceActionMenu = ({ target, - canMutate, + canRename, + canDelete, labels, onDownload, + onSaveAs, onRename, onDelete, onMouseEnter, onMouseLeave, }: { - target: AttachmentMenuTarget; - canMutate: boolean; - labels: { download: string; rename: string; delete: string; unavailable: string }; + target: ResourceMenuTarget; + canRename: boolean; + canDelete: boolean; + labels: { download: string; saveAs: string; rename: string; delete: string; unavailable: string }; onDownload: () => void; + onSaveAs: () => void; onRename: () => void; onDelete: () => void; onMouseEnter: () => void; - onMouseLeave: (event: ReactMouseEvent) => void; + onMouseLeave: () => void; }) => createPortal(
{labels.download} + - @@ -3434,36 +3593,36 @@ const RichEditorPane = ({ { - if (!open && !attachmentActionPending) setAttachmentDialog(null); + if (!open && !resourceActionPending) setResourceDialog(null); }} > - {t("editor.attachmentActions.deleteTitle")} - {t("editor.attachmentActions.deleteDescription")} + {resourceDialogLabels.deleteTitle} + {resourceDialogLabels.deleteDescription}

- {attachmentDialog?.target.filename} + {resourceDialog?.target.filename}

- {attachmentActionError && ( -

{attachmentActionError}

+ {resourceActionError && ( +

{resourceActionError}

)} - -
- {attachmentActionError && !attachmentDialog && ( + {resourceActionError && !resourceDialog && (
- {attachmentActionError} + {resourceActionError}
)} diff --git a/packages/shared/src/i18n/en-US.ts b/packages/shared/src/i18n/en-US.ts index 1a9b2f61..1a7ac826 100644 --- a/packages/shared/src/i18n/en-US.ts +++ b/packages/shared/src/i18n/en-US.ts @@ -581,6 +581,21 @@ export const enUS = { imageSizeLarge: "Large", imageSizeFull: "Full", resizeImage: "Drag to resize image width", + resourceActions: { + download: "Download", + saveAs: "Save as", + rename: "Rename", + delete: "Delete", + unavailable: "This action is available after the resource has synced and the note is editable", + }, + imageActions: { + renameTitle: "Rename image", + renameDescription: "The new filename will be synced to the image resource and the current note.", + filenameLabel: "Filename", + deleteTitle: "Delete image", + deleteDescription: "The image will be permanently removed from storage and this note. This cannot be undone.", + failed: "The image action failed. Try again.", + }, currentNotebook: "Current notebook", close: "Close", searchNotebook: "Search notebooks", @@ -590,15 +605,11 @@ export const enUS = { attachmentLabel: "Attachment: {{filename}}", attachmentInsertText: "Attachment: {{filename}} {{url}}", attachmentActions: { - download: "Download", - rename: "Rename", - delete: "Delete", renameTitle: "Rename attachment", renameDescription: "The new filename will be synced to the attachment and the current note.", filenameLabel: "Filename", deleteTitle: "Delete attachment", deleteDescription: "The attachment will be permanently removed from storage and this note. This cannot be undone.", - unavailableUntilSynced: "This action is available after the attachment has synced", failed: "The attachment action failed. Try again.", }, placeholder: "Start writing...", diff --git a/packages/shared/src/i18n/zh-CN.ts b/packages/shared/src/i18n/zh-CN.ts index 98e8ee1f..3734a5cd 100644 --- a/packages/shared/src/i18n/zh-CN.ts +++ b/packages/shared/src/i18n/zh-CN.ts @@ -581,6 +581,21 @@ export const zhCN = { imageSizeLarge: "较大", imageSizeFull: "铺满", resizeImage: "拖拽调整图片宽度", + resourceActions: { + download: "下载", + saveAs: "另存为", + rename: "重命名", + delete: "删除", + unavailable: "资源同步完成且当前笔记可编辑后才能执行此操作", + }, + imageActions: { + renameTitle: "重命名图片", + renameDescription: "新的文件名会同步到图片资源和当前笔记。", + filenameLabel: "文件名", + deleteTitle: "删除图片", + deleteDescription: "图片会从存储空间和当前笔记中永久删除,此操作无法撤销。", + failed: "图片操作失败,请重试。", + }, currentNotebook: "所在笔记本", close: "关闭", searchNotebook: "搜索笔记本", @@ -590,15 +605,11 @@ export const zhCN = { attachmentLabel: "附件:{{filename}}", attachmentInsertText: "附件:{{filename}} {{url}}", attachmentActions: { - download: "下载", - rename: "重命名", - delete: "删除", renameTitle: "重命名附件", renameDescription: "新的文件名会同步到附件和当前笔记。", filenameLabel: "文件名", deleteTitle: "删除附件", deleteDescription: "附件会从存储空间和当前笔记中永久删除,此操作无法撤销。", - unavailableUntilSynced: "附件同步完成后才能执行此操作", failed: "附件操作失败,请重试。", }, placeholder: "开始记录...",