diff --git a/src/rich-text/plugins/link-editor.ts b/src/rich-text/plugins/link-editor.ts index d88b45e7..cebe4974 100644 --- a/src/rich-text/plugins/link-editor.ts +++ b/src/rich-text/plugins/link-editor.ts @@ -70,7 +70,7 @@ export class LinkEditor extends PluginInterfaceView< - + @@ -100,7 +100,8 @@ export class LinkEditor extends PluginInterfaceView< this.viewContainer.addEventListener("reset", (e) => { e.preventDefault(); e.stopPropagation(); - const tr = this.tryHideInterfaceTr(view.state); + let tr = this.tryHideInterfaceTr(view.state); + tr = LINK_EDITOR_KEY.updateVisibility(false, tr); if (tr) { view.dispatch(tr); } @@ -164,6 +165,8 @@ export class LinkEditor extends PluginInterfaceView< text: null, }) || view.state.tr; + tr = LINK_EDITOR_KEY.updateVisibility(false, tr); + // set the text first, inheriting all marks tr = tr.replaceSelectionWith(node, true); @@ -195,9 +198,9 @@ export class LinkEditor extends PluginInterfaceView< update(view: EditorView): void { super.update(view); + const state = LINK_EDITOR_KEY.getState(view.state); if (this.isShown) { - const state = LINK_EDITOR_KEY.getState(view.state); if (state?.url) { this.hrefInput.value = state.url; this.validate(state.url); @@ -215,8 +218,9 @@ export class LinkEditor extends PluginInterfaceView< this.hrefInput.focus(); } else { this.resetEditor(); - const tr = this.tryHideInterfaceTr(view.state); + let tr = this.tryHideInterfaceTr(view.state); if (tr) { + tr = LINK_EDITOR_KEY.updateVisibility(false, tr); view.dispatch(tr); } } @@ -274,6 +278,11 @@ class LinkEditorPluginKey extends ManagedInterfaceKey { meta.forceHideTooltip = true; return this.setMeta(state.tr, meta); } + updateVisibility(visibility: boolean, trans: Transaction): Transaction { + const meta = trans.getMeta(LINK_EDITOR_KEY) as LinkEditorPluginState; + meta.visible = visibility; + return this.setMeta(trans, meta); + } } /** The plugin key the image uploader plugin is tied to */ @@ -628,7 +637,7 @@ export const linkEditorPlugin = (features: CommonmarkParserFeatures) => shouldShow: false, }; }, - apply(tr, value, _, newState): LinkEditorPluginState { + apply(tr, value, state, newState): LinkEditorPluginState { // check if force hide was set and add to value for getDecorations to use const meta = this.getMeta(tr) || value; if ("forceHideTooltip" in meta) { @@ -636,11 +645,24 @@ export const linkEditorPlugin = (features: CommonmarkParserFeatures) => } // update the linkTooltip and get the decorations - const decorations = value.linkTooltip.getDecorations( + let decorations = value.linkTooltip.getDecorations( value, newState ); + //get decoration for current selection + const sel = state.selection; + let selectionDecoration = null; + + if (meta.visible && sel && sel.from !== sel.to) { + selectionDecoration = Decoration.inline(sel.from, sel.to, { + class: "bg-black-225", + }); + decorations = decorations.add(newState.doc, [ + selectionDecoration, + ]); + } + return { ...meta, forceHideTooltip: @@ -713,11 +735,13 @@ export function showLinkEditor( url?: string, text?: string ): void { - const tr = LINK_EDITOR_KEY.showInterfaceTr(view.state, { + let tr = LINK_EDITOR_KEY.showInterfaceTr(view.state, { url, text, }); + tr = LINK_EDITOR_KEY.updateVisibility(true, tr); + if (tr) { view.dispatch(tr); } @@ -728,11 +752,13 @@ export function showLinkEditor( * @param view The current editor view */ export function hideLinkEditor(view: EditorView): void { - const tr = LINK_EDITOR_KEY.hideInterfaceTr(view.state, { + let tr = LINK_EDITOR_KEY.hideInterfaceTr(view.state, { url: null, text: null, }); + tr = LINK_EDITOR_KEY.updateVisibility(false, tr); + if (tr) { view.dispatch(tr); } diff --git a/test/rich-text/plugins/link-editor.test.ts b/test/rich-text/plugins/link-editor.test.ts index 0074509b..fa499d9a 100644 --- a/test/rich-text/plugins/link-editor.test.ts +++ b/test/rich-text/plugins/link-editor.test.ts @@ -137,6 +137,35 @@ describe("link-editor", () => { expect(inputContainer.classList).not.toContain("has-error"); }); + it("should highlight selected text in grey when link editor is open", () => { + view = richTextView( + "highlight this text", + () => pluginContainer, + () => menuContainer + ); + + // Select the text we want to highlight + view.editorView.updateState( + applySelection(view.editorView.state, 0, 19) // Select "highlight this text" + ); + + showLinkEditor(view.editorView); + editor.update(view.editorView); + + const decorations = getDecorations(view.editorView.state); + const decoration = decorations.find(0, 19)[0]; + + expect(decoration).toBeDefined(); // The highlight should exist + + // Verify the selection is styled with a decoration containing the correct class + const editorContent = view.editorView.dom; + const highlightedText = + editorContent.querySelector(".bg-black-225"); + + expect(highlightedText).toBeTruthy(); + expect(highlightedText.textContent).toBe("highlight this text"); + }); + it("should prefill fields when a link is edited via the tooltip", async () => { view = richTextView( "[link text](https://www.example.com)",