feat: copy note template IDs
This commit is contained in:
1 parent
d06b6531aa
commit
a558062d37
4 files changed
+57
No files matched your search
@@ -2,6 +2,7 @@ import { useMemo, useState, type MouseEvent, type ReactNode } from "react";
|
||||
import {
|
||||
Check,
|
||||
ChevronLeft,
|
||||
Copy,
|
||||
Eye,
|
||||
LayoutList,
|
||||
Pencil,
|
||||
@@ -20,6 +21,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { WORKSPACE_PAGE_TITLE_CLASSNAME } from "@/lib/workspace-ui";
|
||||
import { copyTextToClipboard } from "@/lib/clipboard";
|
||||
import type { MemoTemplate as SavedMemoTemplate } from "@edgeever/shared";
|
||||
|
||||
const TemplateIconAction = ({
|
||||
@@ -86,6 +88,16 @@ export const TemplatesPane = ({
|
||||
} | null>(null);
|
||||
|
||||
const [deleteConfirmTemplate, setDeleteConfirmTemplate] = useState<SavedMemoTemplate | null>(null);
|
||||
const [templateIdCopyNotice, setTemplateIdCopyNotice] = useState<{
|
||||
id: string;
|
||||
status: "copied" | "error";
|
||||
} | null>(null);
|
||||
|
||||
const copyTemplateId = async (template: SavedMemoTemplate) => {
|
||||
const copied = await copyTextToClipboard(template.id);
|
||||
setTemplateIdCopyNotice({ id: template.id, status: copied ? "copied" : "error" });
|
||||
window.setTimeout(() => setTemplateIdCopyNotice(null), copied ? 2200 : 3000);
|
||||
};
|
||||
|
||||
const startEditing = (template: SavedMemoTemplate) => {
|
||||
setCreatingTemplate(false);
|
||||
@@ -354,6 +366,16 @@ export const TemplatesPane = ({
|
||||
|
||||
<div className="mt-4 border-t border-slate-100 pt-3 flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-1 text-slate-400">
|
||||
<TemplateIconAction
|
||||
className="p-1 hover:text-slate-700 transition rounded-md hover:bg-slate-100"
|
||||
label={t("templates.copyId")}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
void copyTemplateId(template);
|
||||
}}
|
||||
>
|
||||
<Copy className="h-3.5 w-3.5" />
|
||||
</TemplateIconAction>
|
||||
<TemplateIconAction
|
||||
className="p-1 hover:text-slate-700 transition rounded-md hover:bg-slate-100"
|
||||
disabled={isCreating}
|
||||
@@ -519,6 +541,20 @@ export const TemplatesPane = ({
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
|
||||
{templateIdCopyNotice && (
|
||||
<div
|
||||
className={`fixed bottom-5 left-1/2 z-[120] max-w-[calc(100vw-2rem)] -translate-x-1/2 truncate rounded-md px-3 py-2 text-sm font-medium text-white shadow-lg ${
|
||||
templateIdCopyNotice.status === "copied" ? "bg-emerald-700" : "bg-rose-600"
|
||||
}`}
|
||||
role={templateIdCopyNotice.status === "copied" ? "status" : "alert"}
|
||||
>
|
||||
{t(
|
||||
templateIdCopyNotice.status === "copied" ? "templates.idCopied" : "templates.idCopyFailed",
|
||||
{ id: templateIdCopyNotice.id },
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
|
||||
@@ -1272,6 +1272,9 @@ export const enUS = {
|
||||
emptyMyTemplates: "No templates yet",
|
||||
emptyMyTemplatesHint: "Click \"New Template\" above or save any note as a template directly in the editor.",
|
||||
delete: "Delete template",
|
||||
copyId: "Copy template ID",
|
||||
idCopied: "Template ID copied: {{id}}",
|
||||
idCopyFailed: "Could not copy the template ID.",
|
||||
deleteConfirmTitle: "Delete template \"{{name}}\"",
|
||||
deleteConfirmDescription: "Are you sure you want to delete this template? This operation cannot be undone.",
|
||||
saveAsTemplate: "Save as template",
|
||||
|
||||
@@ -1272,6 +1272,9 @@ export const zhCN = {
|
||||
emptyMyTemplates: "暂无模板",
|
||||
emptyMyTemplatesHint: "你可以点击上方「新建模板」,或在笔记编辑器中将常用笔记另存为模板。",
|
||||
delete: "删除模板",
|
||||
copyId: "复制模板 ID",
|
||||
idCopied: "模板 ID 已复制:{{id}}",
|
||||
idCopyFailed: "复制模板 ID 失败。",
|
||||
deleteConfirmTitle: "删除模板「{{name}}」",
|
||||
deleteConfirmDescription: "确定要删除该模板吗?此操作不可恢复。",
|
||||
saveAsTemplate: "另存为模板",
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const templatesPaneSource = readFileSync(
|
||||
new URL("../apps/web/src/components/TemplatesPane.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
describe("copy note template ID", () => {
|
||||
test("copies the raw persisted template ID from every template card", () => {
|
||||
expect(templatesPaneSource).toContain("copyTextToClipboard(template.id)");
|
||||
expect(templatesPaneSource).toContain('label={t("templates.copyId")}');
|
||||
expect(templatesPaneSource).toContain('"templates.idCopied"');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user