diff --git a/packages/core/src/editor/BlockNoteExtensions.ts b/packages/core/src/editor/BlockNoteExtensions.ts index d41b64f978..354ec85bda 100644 --- a/packages/core/src/editor/BlockNoteExtensions.ts +++ b/packages/core/src/editor/BlockNoteExtensions.ts @@ -19,6 +19,10 @@ import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlu import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js"; import { KeyboardShortcutsExtension } from "../extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js"; import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin.js"; +import { + DEFAULT_LINK_PROTOCOL, + VALID_LINK_PROTOCOLS, +} from "../extensions/LinkToolbar/protocols.js"; import { NodeSelectionKeyboardPlugin } from "../extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.js"; import { PlaceholderPlugin } from "../extensions/Placeholder/PlaceholderPlugin.js"; import { PreviousBlockTypePlugin } from "../extensions/PreviousBlockType/PreviousBlockTypePlugin.js"; @@ -159,14 +163,9 @@ const getTipTapExtensions = < // marks: Link.extend({ inclusive: false, - addKeyboardShortcuts() { - return { - "Mod-k": () => { - this.editor.commands.toggleLink({ href: "" }); - return true; - }, - }; - }, + }).configure({ + defaultProtocol: DEFAULT_LINK_PROTOCOL, + protocols: VALID_LINK_PROTOCOLS, }), ...Object.values(opts.styleSpecs).map((styleSpec) => { return styleSpec.implementation.mark; diff --git a/packages/core/src/extensions/LinkToolbar/protocols.ts b/packages/core/src/extensions/LinkToolbar/protocols.ts new file mode 100644 index 0000000000..e021b08df4 --- /dev/null +++ b/packages/core/src/extensions/LinkToolbar/protocols.ts @@ -0,0 +1,13 @@ +export const VALID_LINK_PROTOCOLS = [ + "http", + "https", + "ftp", + "ftps", + "mailto", + "tel", + "callto", + "sms", + "cid", + "xmpp", +]; +export const DEFAULT_LINK_PROTOCOL = "https"; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3c3e556eec..20e98bf6cb 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -30,6 +30,7 @@ export * from "./extensions-shared/UiElementPosition.js"; export * from "./extensions/FilePanel/FilePanelPlugin.js"; export * from "./extensions/FormattingToolbar/FormattingToolbarPlugin.js"; export * from "./extensions/LinkToolbar/LinkToolbarPlugin.js"; +export * from "./extensions/LinkToolbar/protocols.js"; export * from "./extensions/SideMenu/SideMenuPlugin.js"; export * from "./extensions/SuggestionMenu/DefaultGridSuggestionItem.js"; export * from "./extensions/SuggestionMenu/DefaultSuggestionItem.js"; diff --git a/packages/react/src/components/FormattingToolbar/DefaultButtons/CreateLinkButton.tsx b/packages/react/src/components/FormattingToolbar/DefaultButtons/CreateLinkButton.tsx index 17340f027b..889eb27c7a 100644 --- a/packages/react/src/components/FormattingToolbar/DefaultButtons/CreateLinkButton.tsx +++ b/packages/react/src/components/FormattingToolbar/DefaultButtons/CreateLinkButton.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { RiLink } from "react-icons/ri"; import { @@ -48,14 +48,31 @@ export const CreateLinkButton = () => { const selectedBlocks = useSelectedBlocks(editor); + const [opened, setOpened] = useState(false); const [url, setUrl] = useState(editor.getSelectedLinkUrl() || ""); const [text, setText] = useState(editor.getSelectedText()); useEditorContentOrSelectionChange(() => { + setOpened(false); setText(editor.getSelectedText() || ""); setUrl(editor.getSelectedLinkUrl() || ""); }, editor); + useEffect(() => { + const callback = (event: KeyboardEvent) => { + if ((event.ctrlKey || event.metaKey) && event.key === "k") { + setOpened(true); + event.preventDefault(); + } + }; + + editor.prosemirrorView.dom.addEventListener("keydown", callback); + + return () => { + editor.prosemirrorView.dom.removeEventListener("keydown", callback); + }; + }, [editor.prosemirrorView.dom]); + const update = useCallback( (url: string, text: string) => { editor.createLink(url, text); @@ -87,7 +104,7 @@ export const CreateLinkButton = () => { } return ( - + {/* TODO: hide tooltip on click */} { dict.generic.ctrl_shortcut )} icon={} + onClick={() => setOpened(true)} /> { + for (const protocol of VALID_LINK_PROTOCOLS) { + if (url.startsWith(protocol)) { + return url; + } + } + + return `${DEFAULT_LINK_PROTOCOL}://${url}`; +}; + export const EditLinkMenuItems = ( props: Pick ) => { @@ -30,7 +41,7 @@ export const EditLinkMenuItems = ( (event: KeyboardEvent) => { if (event.key === "Enter") { event.preventDefault(); - editLink(currentUrl, currentText); + editLink(validateUrl(currentUrl), currentText); } }, [editLink, currentUrl, currentText] @@ -49,7 +60,7 @@ export const EditLinkMenuItems = ( ); const handleSubmit = useCallback( - () => editLink(currentUrl, currentText), + () => editLink(validateUrl(currentUrl), currentText), [editLink, currentUrl, currentText] );