From 0d7cddf7306859b6494fde5c8ebfcd2e613083e2 Mon Sep 17 00:00:00 2001 From: caleb Date: Sun, 28 Jan 2024 10:05:49 -0600 Subject: [PATCH] link tooltip in milkdown editor --- packages/frontend/package.json | 1 + .../MilkdownEditor/MilkdownEditor.tsx | 56 ++++++++++++++++++- pnpm-lock.yaml | 56 +++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index bd4f397..3adb223 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -25,6 +25,7 @@ "@milkdown/plugin-history": "^7.2.4", "@milkdown/plugin-indent": "^7.2.4", "@milkdown/plugin-listener": "^7.2.4", + "@milkdown/plugin-tooltip": "^7.3.3", "@milkdown/plugin-upload": "^7.2.4", "@milkdown/preset-commonmark": "^7.2.4", "@milkdown/prose": "^7.2.4", diff --git a/packages/frontend/src/components/MilkdownEditor/MilkdownEditor.tsx b/packages/frontend/src/components/MilkdownEditor/MilkdownEditor.tsx index 857f247..71a8289 100644 --- a/packages/frontend/src/components/MilkdownEditor/MilkdownEditor.tsx +++ b/packages/frontend/src/components/MilkdownEditor/MilkdownEditor.tsx @@ -2,9 +2,11 @@ import { CmdKey, Editor, defaultValueCtx, + editorViewCtx, editorViewOptionsCtx, rootCtx, } from "@milkdown/core"; +import { Ctx } from "@milkdown/ctx"; import { clipboard } from "@milkdown/plugin-clipboard"; import { cursor } from "@milkdown/plugin-cursor"; import { emoji } from "@milkdown/plugin-emoji"; @@ -16,9 +18,11 @@ import { } from "@milkdown/plugin-history"; import { indent } from "@milkdown/plugin-indent"; import { listener, listenerCtx } from "@milkdown/plugin-listener"; +import { TooltipProvider, tooltipFactory } from "@milkdown/plugin-tooltip"; import { Uploader, upload, uploadConfig } from "@milkdown/plugin-upload"; import { commonmark, + linkSchema, toggleEmphasisCommand, toggleStrongCommand, wrapInBlockquoteCommand, @@ -27,6 +31,8 @@ import { wrapInOrderedListCommand, } from "@milkdown/preset-commonmark"; import { Node, Schema } from "@milkdown/prose/model"; +import { EditorState } from "@milkdown/prose/state"; +import { EditorView } from "@milkdown/prose/view"; import { Milkdown, MilkdownProvider, useEditor } from "@milkdown/react"; import { nord } from "@milkdown/theme-nord"; import { callCommand, replaceAll } from "@milkdown/utils"; @@ -40,6 +46,8 @@ import env from "../../lib/env"; import { Button } from "../ui/button"; import { placeholderCtx, placeholder as placeholderPlugin } from "./plugins"; +const linkTooltip = tooltipFactory("editor-tooltip"); + interface MilkdownEditorProps { onChange?: (value: string) => void; placeholder?: string; @@ -76,6 +84,47 @@ const uploader: Uploader = async (files: FileList, schema: Schema) => { return nodes; }; +function tooltipPluginView(ctx: Ctx) { + return (_view: EditorView) => { + const content = document.createElement("div"); + const provider = new TooltipProvider({ + content, + shouldShow: (view: EditorView) => { + const { selection, doc } = view.state; + const has = doc.rangeHasMark( + selection.from, + selection.to, + linkSchema.type(ctx), + ); + if (has || selection.empty) return false; + + return true; + }, + }); + + content.textContent = "🔗"; + content.className = "link-insert-button"; + content.onmousedown = (e: MouseEvent) => { + e.preventDefault(); + const view = ctx.get(editorViewCtx); + const { selection } = view.state; + ctx.get(linkTooltip.key).addLink(selection.from, selection.to); + provider.hide(); + }; + + return { + update: (updatedView: EditorView, prevState: EditorState) => { + if (ctx.get(linkTooltipState.key).mode === "edit") return; + provider.update(updatedView, prevState); + }, + destroy: () => { + provider.destroy(); + content.remove(); + }, + }; + }; +} + const BaseMilkdownEditor = forwardRef( ({ onChange, placeholder, defaultValue }: MilkdownEditorProps, ref) => { const { get } = useEditor( @@ -120,6 +169,10 @@ const BaseMilkdownEditor = forwardRef( })); ctx.set(placeholderCtx, placeholder ? placeholder : ""); + + ctx.set(linkTooltip.key, { + view: tooltipPluginView(ctx), + }); }) .config(nord) .use(clipboard) @@ -130,7 +183,8 @@ const BaseMilkdownEditor = forwardRef( .use(commonmark) .use(indent) .use(upload) - .use(cursor), + .use(cursor) + .use(linkTooltip), [], ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d6c232..3ef2f7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,6 +58,9 @@ importers: '@milkdown/plugin-listener': specifier: ^7.2.4 version: 7.3.2(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2) + '@milkdown/plugin-tooltip': + specifier: ^7.3.3 + version: 7.3.3(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2) '@milkdown/plugin-upload': specifier: ^7.2.4 version: 7.3.2(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2) @@ -2508,6 +2511,12 @@ packages: tslib: 2.6.2 dev: false + /@milkdown/exception@7.3.3: + resolution: {integrity: sha512-q6S9P5yux23BPy5/GK81OslQuVWD+PrNljb1asv10TTscZAyZmgCMvECQrR3qrwd/Ja5JN5C2CcYLe1JgzI1Fg==} + dependencies: + tslib: 2.6.2 + dev: false + /@milkdown/plugin-clipboard@7.3.2(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2): resolution: {integrity: sha512-/xWhGI5wWMsZhRnN1/CKz9QOlrGz2d+xpGWbFjSooB3SzMULBxoauz+8rrNni6E2I7TqJTfm/8gwtuo1ycpdKg==} peerDependencies: @@ -2612,6 +2621,26 @@ packages: - '@milkdown/transformer' dev: false + /@milkdown/plugin-tooltip@7.3.3(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2): + resolution: {integrity: sha512-RgmEAhh0Y2hx3I94A8+A0iQY8zyGAlaaHnOxvXg+LRiHr8MJ8WxY87+yYW827PBdKjbquGumlo71yTRJXox74w==} + peerDependencies: + '@milkdown/core': ^7.2.0 + '@milkdown/ctx': ^7.2.0 + '@milkdown/prose': ^7.2.0 + dependencies: + '@milkdown/core': 7.3.2(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2) + '@milkdown/ctx': 7.3.2 + '@milkdown/exception': 7.3.3 + '@milkdown/prose': 7.3.2 + '@milkdown/utils': 7.3.3(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2) + '@types/lodash.debounce': 4.0.9 + lodash.debounce: 4.0.8 + tippy.js: 6.3.7 + tslib: 2.6.2 + transitivePeerDependencies: + - '@milkdown/transformer' + dev: false + /@milkdown/plugin-upload@7.3.2(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2): resolution: {integrity: sha512-T6M3cS7Ye23OMs8lGj+mVMmNvOPikwACSQ52UJUI4pnsCaZmWhbmpu0gY6YS17fEzVgtEcUzqEpG/x/5G/Ps8Q==} peerDependencies: @@ -2735,6 +2764,23 @@ packages: tslib: 2.6.2 dev: false + /@milkdown/utils@7.3.3(@milkdown/core@7.3.2)(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2): + resolution: {integrity: sha512-PwM43+YLuvS+RUaiFWZPS6vBxGcEp9nUY2U0h00FHRJjbTSriDHcnTgpkRX5u2JKBaWaRCDTFGjVmfJI6q6LGA==} + peerDependencies: + '@milkdown/core': ^7.2.0 + '@milkdown/ctx': ^7.2.0 + '@milkdown/prose': ^7.2.0 + '@milkdown/transformer': ^7.2.0 + dependencies: + '@milkdown/core': 7.3.2(@milkdown/ctx@7.3.2)(@milkdown/prose@7.3.2)(@milkdown/transformer@7.3.2) + '@milkdown/ctx': 7.3.2 + '@milkdown/exception': 7.3.3 + '@milkdown/prose': 7.3.2 + '@milkdown/transformer': 7.3.2(@milkdown/prose@7.3.2) + nanoid: 5.0.4 + tslib: 2.6.2 + dev: false + /@motionone/animation@10.16.3: resolution: {integrity: sha512-QUGWpLbMFLhyqKlngjZhjtxM8IqiJQjLK0DF+XOF6od9nhSvlaeEpOY/UMCRVcZn/9Tr2rZO22EkuCIjYdI74g==} dependencies: @@ -3362,6 +3408,10 @@ packages: resolution: {integrity: sha512-HaW78NszGzRZd9SeoI3JD11JqY+lubnaOx7Pewj5pfjqWXOEATpeKIFb9Z4t2WBUK2iryiXX3lzWwmYWgUL0Ug==} dev: false + /@popperjs/core@2.11.8: + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + dev: false + /@prisma/client@5.7.0(prisma@5.2.0-dev.71): resolution: {integrity: sha512-cZmglCrfNbYpzUtz7HscVHl38e9CrUs31nrVoGUK1nIPXGgt8hT4jj2s657UXcNdQ/jBUxDgGmHyu2Nyrq1txg==} engines: {node: '>=16.13'} @@ -11689,6 +11739,12 @@ packages: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} dev: false + /tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + dependencies: + '@popperjs/core': 2.11.8 + dev: false + /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'}