Skip to content

Commit 41455fa

Browse files
committed
fix(links): allow to pass custom link handler into editor and use it
The return of the link handler 👻 Required as we want to pass a link handler from Collectives to be used for the "Open link" button and when opening a link via Ctrl-click. Signed-off-by: Jonas <jonas@freesources.org>
1 parent 8a2a1d4 commit 41455fa

7 files changed

Lines changed: 34 additions & 8 deletions

File tree

src/EditorFactory.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ const createRichEditor = ({
4545
relativePath,
4646
isEmbedded = false,
4747
mentionSearch = undefined,
48+
openLink = undefined,
4849
}: {
4950
extensions?: Extension[]
5051
connection?: Connection
5152
relativePath?: string
5253
isEmbedded?: boolean
5354
mentionSearch?: (query: string) => Promise<Record<string, string>>
55+
openLink?: (href: string) => void
5456
} = {}) => {
5557
return new Editor({
5658
editorProps,
@@ -60,6 +62,7 @@ const createRichEditor = ({
6062
relativePath,
6163
isEmbedded,
6264
mentionSearch,
65+
openLink,
6366
}),
6467
FocusTrap,
6568
...extensions,

src/components/Editor.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ import {
100100
ATTACHMENT_RESOLVER,
101101
HOOK_MENTION_SEARCH,
102102
IS_MOBILE,
103+
OPEN_LINK_HANDLER,
103104
} from './Editor.provider.ts'
104105
import ReadonlyBar from './Menu/ReadonlyBar.vue'
105106
@@ -128,6 +129,7 @@ import {
128129
serializePlainText,
129130
} from './../EditorFactory.ts'
130131
import { createMarkdownSerializer } from './../extensions/Markdown.js'
132+
import { openLink as defaultOpenLink } from './../helpers/links.js'
131133
import markdownit from './../markdownit/index.js'
132134
import isMobile from './../mixins/isMobile.js'
133135
import AttachmentResolver from './../services/AttachmentResolver.js'
@@ -270,13 +272,17 @@ export default defineComponent({
270272
CollaborationCaret.configure({ provider: { awareness } }),
271273
]
272274
const mentionSearch = inject(HOOK_MENTION_SEARCH)
275+
const openLinkHandler = inject(OPEN_LINK_HANDLER, {
276+
openLink: defaultOpenLink,
277+
})
273278
const editor = isRichEditor
274279
? createRichEditor({
275280
connection,
276281
relativePath: props.relativePath,
277282
extensions,
278283
isEmbedded: props.isEmbedded,
279284
mentionSearch,
285+
openLink: openLinkHandler.openLink,
280286
})
281287
: createPlainEditor({ language, extensions })
282288
provideEditor(editor)

src/components/Editor/MarkdownContentEditor.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ import Wrapper from './Wrapper.vue'
2525
/* eslint-disable import/no-named-as-default */
2626
import { getCurrentUser } from '@nextcloud/auth'
2727
import { UndoRedo } from '@tiptap/extensions'
28-
import { provide, watch } from 'vue'
28+
import { inject, provide, watch } from 'vue'
2929
import { provideEditor } from '../../composables/useEditor.ts'
3030
import { editorFlagsKey } from '../../composables/useEditorFlags.ts'
3131
import { provideEditorHeadings } from '../../composables/useEditorHeadings.ts'
3232
import { useEditorMethods } from '../../composables/useEditorMethods.ts'
3333
import { provideEditorWidth } from '../../composables/useEditorWidth.ts'
3434
import { FocusTrap, RichText } from '../../extensions/index.js'
3535
import { createMarkdownSerializer } from '../../extensions/Markdown.js'
36+
import { openLink as defaultOpenLink } from '../../helpers/links.js'
3637
import AttachmentResolver from '../../services/AttachmentResolver.js'
37-
import { ATTACHMENT_RESOLVER } from '../Editor.provider.ts'
38+
import { ATTACHMENT_RESOLVER, OPEN_LINK_HANDLER } from '../Editor.provider.ts'
3839
import ReadonlyBar from '../Menu/ReadonlyBar.vue'
3940
import ContentContainer from './ContentContainer.vue'
4041
@@ -82,9 +83,13 @@ export default {
8283
emits: ['update:content'],
8384
8485
setup(props) {
86+
const openLinkHandler = inject(OPEN_LINK_HANDLER, {
87+
openLink: defaultOpenLink,
88+
})
8589
const extensions = [
8690
RichText.configure({
8791
extensions: [UndoRedo],
92+
openLink: openLinkHandler.openLink,
8893
}),
8994
FocusTrap,
9095
]

src/components/Editor/PreviewOptions.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import DotsVerticalIcon from 'vue-material-design-icons/DotsVertical.vue'
7373
import OpenIcon from 'vue-material-design-icons/OpenInNew.vue'
7474
import DeleteOutlineIcon from 'vue-material-design-icons/TrashCanOutline.vue'
7575
import CopyToClipboardMixin from '../../mixins/CopyToClipboardMixin.js'
76+
import { useOpenLinkHandler } from '../Editor.provider.ts'
7677
7778
export default {
7879
name: 'PreviewOptions',
@@ -91,7 +92,7 @@ export default {
9192
OpenIcon,
9293
},
9394
94-
mixins: [CopyToClipboardMixin],
95+
mixins: [CopyToClipboardMixin, useOpenLinkHandler],
9596
9697
props: {
9798
type: {
@@ -126,7 +127,7 @@ export default {
126127
},
127128
openLink() {
128129
if (!this.href) return
129-
window.open(this.href, '_blank').focus()
130+
this.$openLinkHandler.openLink(this.href)
130131
},
131132
async copyLink() {
132133
await this.copyToClipboard(this.href)

src/extensions/RichText.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default Extension.create({
6969
relativePath: null,
7070
isEmbedded: false,
7171
mentionSearch: undefined,
72+
openLink: undefined,
7273
}
7374
},
7475

@@ -131,6 +132,7 @@ export default Extension.create({
131132
openOnClick: true,
132133
shouldAutoLink: (href) => /^https?:\/\//.test(href),
133134
relativePath: this.options.relativePath,
135+
openLink: this.options.openLink,
134136
}),
135137
LinkBubble,
136138
this.options.editing

src/marks/Link.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const extractHrefFromMarkdownLink = (match: ExtendedRegExpMatchArray) => {
3030

3131
export interface RelativePathLinkOptions extends LinkOptions {
3232
relativePath?: string
33+
openLink?: (href: string) => void
3334
}
3435

3536
const parentDefaults: LinkOptions = {
@@ -95,6 +96,7 @@ const Link = TipTapLink.extend<RelativePathLinkOptions>({
9596
return {
9697
...this.parent?.(),
9798
relativePath: undefined,
99+
openLink: undefined,
98100
...parentDefaults,
99101
}
100102
},
@@ -237,7 +239,7 @@ const Link = TipTapLink.extend<RelativePathLinkOptions>({
237239
.filter((plugin) => !plugin.props.handleClick)
238240

239241
// Add our own click handler plugin
240-
return [...plugins, linkClicking()]
242+
return [...plugins, linkClicking(this.options.openLink)]
241243
},
242244
})
243245

src/plugins/links.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,14 @@ export const linkClickingKey = new PluginKey('textHandleClickLink')
137137
* - Open link in new tab on middle click rather than pasting.
138138
* - Only open link on ctrl/cmd + left click.
139139
* We use the link bubble otherwise.
140+
*
141+
* @param openLink - the openLink callback function
140142
*/
141-
export function linkClicking() {
143+
export function linkClicking(
144+
openLink: (href: string) => void = (href) => {
145+
window.open(href, '_blank')
146+
},
147+
) {
142148
return new Plugin({
143149
key: linkClickingKey,
144150
props: {
@@ -155,6 +161,7 @@ export function linkClicking() {
155161
) {
156162
event.preventDefault()
157163
event.stopImmediatePropagation()
164+
// Open link in new tab on middle click (ignore custom link handler on purpose)
158165
window.open(linkEl.href, '_blank')
159166
}
160167
},
@@ -187,8 +194,8 @@ export function linkClicking() {
187194
// Open anchor links directly
188195
location.href = linkEl.href
189196
} else if (event.ctrlKey || event.metaKey) {
190-
// Open link in new tab on Ctrl/Cmd + left click
191-
window.open(linkEl.href, '_blank')
197+
// Open link directly on Ctrl/Cmd + left click
198+
openLink(linkEl.href)
192199
}
193200
}
194201
},

0 commit comments

Comments
 (0)