Skip to content

Commit 5e4389c

Browse files
fix: Link UX (#1304)
* Changed how keyboard shortcut works and fixed redirects * Fixed lint * Changed default protocol to https * Added protocols list * Made protocol prefix get added to the `url` prop instead of only on render * Fixed lint
1 parent c689e4b commit 5e4389c

File tree

5 files changed

+54
-12
lines changed

5 files changed

+54
-12
lines changed

packages/core/src/editor/BlockNoteExtensions.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlu
1919
import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js";
2020
import { KeyboardShortcutsExtension } from "../extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js";
2121
import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin.js";
22+
import {
23+
DEFAULT_LINK_PROTOCOL,
24+
VALID_LINK_PROTOCOLS,
25+
} from "../extensions/LinkToolbar/protocols.js";
2226
import { NodeSelectionKeyboardPlugin } from "../extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.js";
2327
import { PlaceholderPlugin } from "../extensions/Placeholder/PlaceholderPlugin.js";
2428
import { PreviousBlockTypePlugin } from "../extensions/PreviousBlockType/PreviousBlockTypePlugin.js";
@@ -159,14 +163,9 @@ const getTipTapExtensions = <
159163
// marks:
160164
Link.extend({
161165
inclusive: false,
162-
addKeyboardShortcuts() {
163-
return {
164-
"Mod-k": () => {
165-
this.editor.commands.toggleLink({ href: "" });
166-
return true;
167-
},
168-
};
169-
},
166+
}).configure({
167+
defaultProtocol: DEFAULT_LINK_PROTOCOL,
168+
protocols: VALID_LINK_PROTOCOLS,
170169
}),
171170
...Object.values(opts.styleSpecs).map((styleSpec) => {
172171
return styleSpec.implementation.mark;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const VALID_LINK_PROTOCOLS = [
2+
"http",
3+
"https",
4+
"ftp",
5+
"ftps",
6+
"mailto",
7+
"tel",
8+
"callto",
9+
"sms",
10+
"cid",
11+
"xmpp",
12+
];
13+
export const DEFAULT_LINK_PROTOCOL = "https";

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export * from "./extensions-shared/UiElementPosition.js";
3636
export * from "./extensions/FilePanel/FilePanelPlugin.js";
3737
export * from "./extensions/FormattingToolbar/FormattingToolbarPlugin.js";
3838
export * from "./extensions/LinkToolbar/LinkToolbarPlugin.js";
39+
export * from "./extensions/LinkToolbar/protocols.js";
3940
export * from "./extensions/SideMenu/SideMenuPlugin.js";
4041
export * from "./extensions/SuggestionMenu/DefaultGridSuggestionItem.js";
4142
export * from "./extensions/SuggestionMenu/DefaultSuggestionItem.js";

packages/react/src/components/FormattingToolbar/DefaultButtons/CreateLinkButton.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useMemo, useState } from "react";
1+
import { useCallback, useEffect, useMemo, useState } from "react";
22
import { RiLink } from "react-icons/ri";
33

44
import {
@@ -48,14 +48,31 @@ export const CreateLinkButton = () => {
4848

4949
const selectedBlocks = useSelectedBlocks(editor);
5050

51+
const [opened, setOpened] = useState(false);
5152
const [url, setUrl] = useState<string>(editor.getSelectedLinkUrl() || "");
5253
const [text, setText] = useState<string>(editor.getSelectedText());
5354

5455
useEditorContentOrSelectionChange(() => {
56+
setOpened(false);
5557
setText(editor.getSelectedText() || "");
5658
setUrl(editor.getSelectedLinkUrl() || "");
5759
}, editor);
5860

61+
useEffect(() => {
62+
const callback = (event: KeyboardEvent) => {
63+
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
64+
setOpened(true);
65+
event.preventDefault();
66+
}
67+
};
68+
69+
editor.prosemirrorView.dom.addEventListener("keydown", callback);
70+
71+
return () => {
72+
editor.prosemirrorView.dom.removeEventListener("keydown", callback);
73+
};
74+
}, [editor.prosemirrorView.dom]);
75+
5976
const update = useCallback(
6077
(url: string, text: string) => {
6178
editor.createLink(url, text);
@@ -87,7 +104,7 @@ export const CreateLinkButton = () => {
87104
}
88105

89106
return (
90-
<Components.Generic.Popover.Root>
107+
<Components.Generic.Popover.Root opened={opened}>
91108
<Components.Generic.Popover.Trigger>
92109
{/* TODO: hide tooltip on click */}
93110
<Components.FormattingToolbar.Button
@@ -100,6 +117,7 @@ export const CreateLinkButton = () => {
100117
dict.generic.ctrl_shortcut
101118
)}
102119
icon={<RiLink />}
120+
onClick={() => setOpened(true)}
103121
/>
104122
</Components.Generic.Popover.Trigger>
105123
<Components.Generic.Popover.Content

packages/react/src/components/LinkToolbar/EditLinkMenuItems.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DEFAULT_LINK_PROTOCOL, VALID_LINK_PROTOCOLS } from "@blocknote/core";
12
import {
23
ChangeEvent,
34
KeyboardEvent,
@@ -10,6 +11,16 @@ import { useComponentsContext } from "../../editor/ComponentsContext.js";
1011
import { useDictionary } from "../../i18n/dictionary.js";
1112
import { LinkToolbarProps } from "./LinkToolbarProps.js";
1213

14+
const validateUrl = (url: string) => {
15+
for (const protocol of VALID_LINK_PROTOCOLS) {
16+
if (url.startsWith(protocol)) {
17+
return url;
18+
}
19+
}
20+
21+
return `${DEFAULT_LINK_PROTOCOL}://${url}`;
22+
};
23+
1324
export const EditLinkMenuItems = (
1425
props: Pick<LinkToolbarProps, "url" | "text" | "editLink">
1526
) => {
@@ -30,7 +41,7 @@ export const EditLinkMenuItems = (
3041
(event: KeyboardEvent) => {
3142
if (event.key === "Enter") {
3243
event.preventDefault();
33-
editLink(currentUrl, currentText);
44+
editLink(validateUrl(currentUrl), currentText);
3445
}
3546
},
3647
[editLink, currentUrl, currentText]
@@ -49,7 +60,7 @@ export const EditLinkMenuItems = (
4960
);
5061

5162
const handleSubmit = useCallback(
52-
() => editLink(currentUrl, currentText),
63+
() => editLink(validateUrl(currentUrl), currentText),
5364
[editLink, currentUrl, currentText]
5465
);
5566

0 commit comments

Comments
 (0)