Skip to content

Commit 3e4c847

Browse files
committed
feat(link-editor): change link editor from a popover to a plugin pane (#142)
fixes #19
1 parent 768b575 commit 3e4c847

File tree

11 files changed

+1104
-490
lines changed

11 files changed

+1104
-490
lines changed

src/rich-text/commands/index.ts

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import {
1818
imageUploaderEnabled,
1919
showImageUploader,
2020
} from "../../shared/prosemirror-plugins/image-upload";
21+
import { getCurrentTextNode, getShortcut } from "../../shared/utils";
2122
import type { CommonViewOptions } from "../../shared/view";
22-
import { getShortcut } from "../../shared/utils";
23-
import { LINK_TOOLTIP_KEY } from "../plugins/link-editor";
23+
import { showLinkEditor } from "../plugins/link-editor";
2424
import { insertParagraphIfAtDocEnd } from "./helpers";
2525
import {
2626
insertTableColumnAfterCommand,
@@ -198,30 +198,52 @@ export function insertLinkCommand(
198198
dispatch: (tr: Transaction) => void,
199199
view: EditorView
200200
): boolean {
201-
if (state.selection.empty) return false;
201+
// never actually toggle the mark, as that is done in the link editor
202+
// we do want to *pretend* to, as toggleMark checks for validity
203+
const valid = toggleMark(state.schema.marks.link, { href: null })(
204+
state,
205+
null
206+
);
202207

203-
let linkUrl = null;
208+
if (dispatch && valid) {
209+
let selectedText: string;
210+
let linkUrl: string;
204211

205-
if (dispatch) {
206-
const selectedText =
207-
state.selection.content().content.firstChild?.textContent ?? null;
208-
const linkMatch = /^http(s)?:\/\/\S+$/.exec(selectedText);
209-
linkUrl = linkMatch?.length > 0 ? linkMatch[0] : "";
210-
211-
// wrap the dispatch function so that we can add additional transactions after toggleMark
212-
const oldDispatch = dispatch;
213-
dispatch = (tr) => {
214-
oldDispatch(tr);
215-
view.dispatch(
216-
LINK_TOOLTIP_KEY.setEditMode(true, state, view.state.tr)
212+
const $anchor = state.selection.$anchor;
213+
// if selection is empty, but inside link mark, use the link url/text from it
214+
if (state.selection.empty && $anchor.textOffset) {
215+
const currentTextNode = getCurrentTextNode(state);
216+
const mark = currentTextNode.marks.find(
217+
(m) => m.type === state.schema.marks.link
217218
);
218-
};
219+
if (mark) {
220+
selectedText = currentTextNode.text;
221+
linkUrl = mark.attrs.href as string;
222+
223+
// expand the selection so we're editing the entire link
224+
const pos = $anchor.pos;
225+
dispatch(
226+
state.tr.setSelection(
227+
TextSelection.create(
228+
state.doc,
229+
pos - $anchor.textOffset,
230+
pos - $anchor.textOffset + selectedText.length
231+
)
232+
)
233+
);
234+
}
235+
} else {
236+
selectedText =
237+
state.selection.content().content.firstChild?.textContent ??
238+
null;
239+
const linkMatch = /^http(s)?:\/\/\S+$/.exec(selectedText);
240+
linkUrl = linkMatch?.length > 0 ? linkMatch[0] : "";
241+
}
242+
243+
showLinkEditor(view, linkUrl, selectedText);
219244
}
220245

221-
return toggleMark(state.schema.marks.link, { href: linkUrl })(
222-
state,
223-
dispatch
224-
);
246+
return valid;
225247
}
226248

227249
/**

src/rich-text/editor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { TagLink } from "./node-views/tag-link";
3333
import { codePasteHandler } from "./plugins/code-paste-handler";
3434
import { linkPasteHandler } from "./plugins/link-paste-handler";
3535
import { linkPreviewPlugin, LinkPreviewProvider } from "./plugins/link-preview";
36-
import { linkTooltipPlugin } from "./plugins/link-editor";
36+
import { linkEditorPlugin } from "./plugins/link-editor";
3737
import { placeholderPlugin } from "../shared/prosemirror-plugins/placeholder";
3838
import { plainTextPasteHandler } from "./plugins/plain-text-paste-handler";
3939
import { spoilerToggle } from "./plugins/spoiler-toggle";
@@ -125,7 +125,7 @@ export class RichTextEditor extends BaseView {
125125
interfaceManagerPlugin(
126126
this.options.pluginParentContainer
127127
),
128-
linkTooltipPlugin(this.options.parserFeatures),
128+
linkEditorPlugin(this.options.parserFeatures),
129129
placeholderPlugin(this.options.placeholderText),
130130
richTextImageUpload(
131131
this.options.imageUpload,

0 commit comments

Comments
 (0)