mirror of
https://github.com/docmost/docmost.git
synced 2026-05-07 06:23:06 +08:00
cleanup
This commit is contained in:
@@ -8,13 +8,10 @@ import {
|
|||||||
} from "@/features/editor/components/table/types/types.ts";
|
} from "@/features/editor/components/table/types/types.ts";
|
||||||
import { ActionIcon, Tooltip } from "@mantine/core";
|
import { ActionIcon, Tooltip } from "@mantine/core";
|
||||||
import {
|
import {
|
||||||
IconDownload,
|
|
||||||
IconPaperclip,
|
IconPaperclip,
|
||||||
IconTrash,
|
IconTrash,
|
||||||
} from "@tabler/icons-react";
|
} from "@tabler/icons-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { getFileUrl } from "@/lib/config.ts";
|
|
||||||
import { isInternalFileUrl } from "@docmost/editor-ext";
|
|
||||||
import classes from "../common/toolbar-menu.module.css";
|
import classes from "../common/toolbar-menu.module.css";
|
||||||
|
|
||||||
export function PdfMenu({ editor }: EditorMenuProps) {
|
export function PdfMenu({ editor }: EditorMenuProps) {
|
||||||
@@ -40,11 +37,15 @@ export function PdfMenu({ editor }: EditorMenuProps) {
|
|||||||
|
|
||||||
const shouldShow = useCallback(
|
const shouldShow = useCallback(
|
||||||
({ state }: ShouldShowProps) => {
|
({ state }: ShouldShowProps) => {
|
||||||
if (!state) {
|
if (!state || !editor.isActive("pdf")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return editor.isActive("pdf") && editor.getAttributes("pdf").src;
|
const { selection } = state;
|
||||||
|
const dom = editor.view.nodeDOM(selection.from) as HTMLElement | null;
|
||||||
|
if (!dom) return false;
|
||||||
|
|
||||||
|
return !!dom.querySelector("[data-pdf-error]");
|
||||||
},
|
},
|
||||||
[editor],
|
[editor],
|
||||||
);
|
);
|
||||||
@@ -71,15 +72,6 @@ export function PdfMenu({ editor }: EditorMenuProps) {
|
|||||||
};
|
};
|
||||||
}, [editor]);
|
}, [editor]);
|
||||||
|
|
||||||
const handleDownload = useCallback(() => {
|
|
||||||
if (!editorState?.src || !isInternalFileUrl(editorState.src)) return;
|
|
||||||
const url = getFileUrl(editorState.src);
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = url;
|
|
||||||
a.download = "";
|
|
||||||
a.click();
|
|
||||||
}, [editorState?.src]);
|
|
||||||
|
|
||||||
const handleConvertToAttachment = useCallback(() => {
|
const handleConvertToAttachment = useCallback(() => {
|
||||||
if (!editorState?.src) return;
|
if (!editorState?.src) return;
|
||||||
|
|
||||||
@@ -124,17 +116,6 @@ export function PdfMenu({ editor }: EditorMenuProps) {
|
|||||||
shouldShow={shouldShow}
|
shouldShow={shouldShow}
|
||||||
>
|
>
|
||||||
<div className={classes.toolbar}>
|
<div className={classes.toolbar}>
|
||||||
<Tooltip position="top" label={t("Download")} withinPortal={false}>
|
|
||||||
<ActionIcon
|
|
||||||
onClick={handleDownload}
|
|
||||||
size="lg"
|
|
||||||
aria-label={t("Download")}
|
|
||||||
variant="subtle"
|
|
||||||
>
|
|
||||||
<IconDownload size={18} />
|
|
||||||
</ActionIcon>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip position="top" label={t("Convert to attachment")} withinPortal={false}>
|
<Tooltip position="top" label={t("Convert to attachment")} withinPortal={false}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
onClick={handleConvertToAttachment}
|
onClick={handleConvertToAttachment}
|
||||||
|
|||||||
@@ -52,10 +52,32 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clickOverlay {
|
.hoverMenu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
top: 56px;
|
||||||
cursor: pointer;
|
right: 8px;
|
||||||
|
z-index: 2;
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoverMenu::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hoverMenu:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdfResizeWrapper:hover .hoverMenu {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pdfError {
|
.pdfError {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NodeViewProps, NodeViewWrapper } from "@tiptap/react";
|
import { NodeViewProps, NodeViewWrapper } from "@tiptap/react";
|
||||||
import { Group, Loader, Text } from "@mantine/core";
|
import { ActionIcon, Group, Loader, Text, Tooltip } from "@mantine/core";
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
import { getFileUrl } from "@/lib/config.ts";
|
import { getFileUrl } from "@/lib/config.ts";
|
||||||
import { ResizableWrapper } from "../common/resizable-wrapper";
|
import { ResizableWrapper } from "../common/resizable-wrapper";
|
||||||
@@ -7,7 +7,11 @@ import clsx from "clsx";
|
|||||||
import classes from "./pdf-view.module.css";
|
import classes from "./pdf-view.module.css";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { isInternalFileUrl } from "@docmost/editor-ext";
|
import { isInternalFileUrl } from "@docmost/editor-ext";
|
||||||
import { IconFileTypePdf } from "@tabler/icons-react";
|
import {
|
||||||
|
IconFileTypePdf,
|
||||||
|
IconPaperclip,
|
||||||
|
IconTrash,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
|
||||||
export default function PdfView(props: NodeViewProps) {
|
export default function PdfView(props: NodeViewProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -34,6 +38,38 @@ export default function PdfView(props: NodeViewProps) {
|
|||||||
[updateAttributes],
|
[updateAttributes],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleConvertToAttachment = useCallback(() => {
|
||||||
|
if (!src) return;
|
||||||
|
const pos = getPos();
|
||||||
|
if (pos === undefined) return;
|
||||||
|
const currentNode = editor.state.doc.nodeAt(pos);
|
||||||
|
if (!currentNode || currentNode.type.name !== "pdf") return;
|
||||||
|
|
||||||
|
editor
|
||||||
|
.chain()
|
||||||
|
.insertContentAt(
|
||||||
|
{ from: pos, to: pos + currentNode.nodeSize },
|
||||||
|
{
|
||||||
|
type: "attachment",
|
||||||
|
attrs: {
|
||||||
|
url: currentNode.attrs.src,
|
||||||
|
name: currentNode.attrs.name,
|
||||||
|
attachmentId: currentNode.attrs.attachmentId,
|
||||||
|
size: currentNode.attrs.size,
|
||||||
|
mime: "application/pdf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}, [editor, src, getPos]);
|
||||||
|
|
||||||
|
const handleDelete = useCallback(() => {
|
||||||
|
const pos = getPos();
|
||||||
|
if (pos === undefined) return;
|
||||||
|
editor.commands.setNodeSelection(pos);
|
||||||
|
editor.commands.deleteSelection();
|
||||||
|
}, [editor, getPos]);
|
||||||
|
|
||||||
if (!src || !safeSrc) {
|
if (!src || !safeSrc) {
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper data-drag-handle>
|
<NodeViewWrapper data-drag-handle>
|
||||||
@@ -54,7 +90,7 @@ export default function PdfView(props: NodeViewProps) {
|
|||||||
if (hasError) {
|
if (hasError) {
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper data-drag-handle>
|
<NodeViewWrapper data-drag-handle>
|
||||||
<div className={classes.pdfError} onClick={handleSelect}>
|
<div data-pdf-error className={clsx(classes.pdfError, { "ProseMirror-selectednode": selected })} onClick={handleSelect}>
|
||||||
<IconFileTypePdf size={32} stroke={1.5} />
|
<IconFileTypePdf size={32} stroke={1.5} />
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{t("Failed to load PDF")}
|
{t("Failed to load PDF")}
|
||||||
@@ -99,7 +135,32 @@ export default function PdfView(props: NodeViewProps) {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{!selected && <div className={classes.clickOverlay} onClick={handleSelect} />}
|
{editor.isEditable && (
|
||||||
|
<div className={classes.hoverMenu}>
|
||||||
|
<Tooltip position="top" label={t("Convert to attachment")} withinPortal>
|
||||||
|
<ActionIcon
|
||||||
|
size="sm"
|
||||||
|
variant="filled"
|
||||||
|
color="dark"
|
||||||
|
onClick={handleConvertToAttachment}
|
||||||
|
aria-label={t("Convert to attachment")}
|
||||||
|
>
|
||||||
|
<IconPaperclip size={14} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip position="top" label={t("Delete")} withinPortal>
|
||||||
|
<ActionIcon
|
||||||
|
size="sm"
|
||||||
|
variant="filled"
|
||||||
|
color="dark"
|
||||||
|
onClick={handleDelete}
|
||||||
|
aria-label={t("Delete")}
|
||||||
|
>
|
||||||
|
<IconTrash size={14} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</ResizableWrapper>
|
</ResizableWrapper>
|
||||||
</div>
|
</div>
|
||||||
</NodeViewWrapper>
|
</NodeViewWrapper>
|
||||||
|
|||||||
@@ -427,7 +427,7 @@ const CommandGroups: SlashMenuGroupedItemsType = {
|
|||||||
editor.chain().focus().deleteRange(range).setDrawio().run(),
|
editor.chain().focus().deleteRange(range).setDrawio().run(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Excalidraw diagram (Whiteboard)",
|
title: "Excalidraw (Whiteboard)",
|
||||||
description: "Draw and sketch excalidraw diagrams",
|
description: "Draw and sketch excalidraw diagrams",
|
||||||
searchTerms: ["diagrams", "draw", "sketch", "whiteboard"],
|
searchTerms: ["diagrams", "draw", "sketch", "whiteboard"],
|
||||||
icon: IconExcalidraw,
|
icon: IconExcalidraw,
|
||||||
|
|||||||
Reference in New Issue
Block a user