Skip to content

Commit 10dcf3f

Browse files
mejo-backportbot[bot]
authored andcommitted
chore(links): migrate links plugin to Typescript
Signed-off-by: Jonas <jonas@freesources.org>
1 parent 0c74061 commit 10dcf3f

3 files changed

Lines changed: 34 additions & 27 deletions

File tree

src/extensions/LinkBubble.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { Extension } from '@tiptap/core'
7-
import { hideLinkBubble, linkBubble } from '../plugins/links.js'
7+
import { hideLinkBubble, linkBubble } from '../plugins/links.ts'
88

99
const LinkBubble = Extension.create({
1010
name: 'linkViewBubble',
Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6+
import type { Editor } from '@tiptap/core'
7+
import type { ResolvedPos } from '@tiptap/pm/model'
8+
import type { Command } from '@tiptap/pm/state'
9+
610
import { Plugin, PluginKey } from '@tiptap/pm/state'
711
import { isLinkToSelfWithHash } from '../helpers/links.js'
812
import LinkBubblePluginView from './LinkBubblePluginView.js'
@@ -12,25 +16,27 @@ import { activeLinkFromSelection } from './linkHelpers.js'
1216

1317
/* Set resolved to be the active element (if it has a link mark)
1418
*
15-
* @params {ResolvedPos} resolved position of the action
19+
* @params resolved - resolved position of the action
1620
*/
17-
export const setActiveLink = (resolved) => (state, dispatch) => {
18-
const mark = resolved.marks().find((m) => m.type.name === 'link')
19-
if (!mark) {
20-
return false
21-
}
22-
const nodeStart = resolved.pos - resolved.textOffset
23-
const active = { mark, nodeStart }
24-
if (dispatch) {
25-
dispatch(state.tr.setMeta(linkBubbleKey, { active }))
21+
export const setActiveLink =
22+
(resolved: ResolvedPos): Command =>
23+
(state, dispatch) => {
24+
const mark = resolved.marks().find((m) => m.type.name === 'link')
25+
if (!mark) {
26+
return false
27+
}
28+
const nodeStart = resolved.pos - resolved.textOffset
29+
const active = { mark, nodeStart }
30+
if (dispatch) {
31+
dispatch(state.tr.setMeta(linkBubbleKey, { active }))
32+
}
33+
return true
2634
}
27-
return true
28-
}
2935

3036
/* Hide the link bubble by setting active state to null
3137
*
3238
*/
33-
export const hideLinkBubble = (state, dispatch) => {
39+
export const hideLinkBubble: Command = (state, dispatch) => {
3440
const pluginState = linkBubbleKey.getState(state)
3541
if (!pluginState?.active) {
3642
return false
@@ -44,10 +50,12 @@ export const hideLinkBubble = (state, dispatch) => {
4450
export const linkBubbleKey = new PluginKey('linkBubble')
4551
/**
4652
* Prosemirror link bubble plugin
47-
* @param {object} options - options for the link bubble plugin view
53+
*
54+
* @param options - options for the link bubble plugin view
55+
* @param options.editor - the editor
4856
*/
49-
export function linkBubble(options) {
50-
const linkBubblePlugin = new Plugin({
57+
export function linkBubble(options: { editor: Editor }) {
58+
const linkBubblePlugin: Plugin = new Plugin({
5159
key: linkBubbleKey,
5260
state: {
5361
init: () => ({ active: null }),
@@ -79,7 +87,7 @@ export function linkBubble(options) {
7987
const sameDoc = oldState?.doc.eq(state.doc)
8088
// Don't open bubble on changes by other session members
8189
const noHistory = transactions.every(
82-
(tr) => tr.meta.addToHistory === false,
90+
(tr) => tr.getMeta('addToHistory') === false,
8391
)
8492
if (sameSelection && (noHistory || sameDoc)) {
8593
return
@@ -137,33 +145,32 @@ export function linkClicking() {
137145
handleDOMEvents: {
138146
// Open link in new tab on middle click
139147
auxclick: (view, event) => {
148+
const linkEl = (event.target as Element | null)?.closest('a')
140149
if (
141-
event.target.closest('a')
150+
linkEl
142151
&& event.button === 1
143152
&& !event.ctrlKey
144153
&& !event.metaKey
145154
&& !event.shiftKey
146155
) {
147156
event.preventDefault()
148157
event.stopImmediatePropagation()
149-
150-
const linkElement = event.target.closest('a')
151-
window.open(linkElement.href, '_blank')
158+
window.open(linkEl.href, '_blank')
152159
}
153160
},
154161
// Prevent paste into links
155162
// On Linux, middle click pastes, which breaks "open in new tab" on middle click
156163
// Pasting into links will break the link anyway, so just disable it altogether.
157164
paste: (view, event) => {
158-
if (event.target.closest('a')) {
165+
if ((event.target as Element | null)?.closest('a')) {
159166
event.stopPropagation()
160167
event.preventDefault()
161168
event.stopImmediatePropagation()
162169
}
163170
},
164171
// Prevent open link for text-only links on left click. Required for read-only mode.
165172
click: (view, event) => {
166-
const linkEl = event.target.closest('a')
173+
const linkEl = (event.target as Element | null)?.closest('a')
167174
// Only text-only links need special handling (e.g. don't handle links inside preview or mermaid diagrams)
168175
if (
169176
!linkEl
@@ -176,9 +183,9 @@ export function linkClicking() {
176183
// Stop browser from opening the link
177184
event.preventDefault()
178185

179-
if (isLinkToSelfWithHash(linkEl.attributes.href?.value)) {
186+
if (isLinkToSelfWithHash(linkEl.href)) {
180187
// Open anchor links directly
181-
location.href = linkEl.attributes.href.value
188+
location.href = linkEl.href
182189
} else if (event.ctrlKey || event.metaKey) {
183190
// Open link in new tab on Ctrl/Cmd + left click
184191
window.open(linkEl.href, '_blank')

src/tests/plugins/linkBubble.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { EditorState, Plugin } from '@tiptap/pm/state'
77
import { schema } from 'prosemirror-schema-basic'
8-
import { hideLinkBubble, linkBubble, setActiveLink } from '../../plugins/links.js'
8+
import { hideLinkBubble, linkBubble, setActiveLink } from '../../plugins/links.ts'
99

1010
describe('linkBubble prosemirror plugin', () => {
1111
test('signature', () => {

0 commit comments

Comments
 (0)