Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
710b350
refactor(links): move linkBubble plugin to plugins/links
max-nextcloud Mar 15, 2024
e6049b0
refactor(links): pass editor via options to LinkBubblePluginView
max-nextcloud Mar 15, 2024
d871277
refactor(links): untangle LinkBubblePluginView from editor
max-nextcloud Mar 15, 2024
e4340f8
refactor(links): use prosemirror tr and state to track clicks
max-nextcloud Mar 17, 2024
90b99be
refactor(links): move click handling into link bubble plugin
max-nextcloud Mar 17, 2024
356509b
refactor(links): only hand link mark to updateTooltip
max-nextcloud Mar 17, 2024
e30c78c
refactor(links): introduce setActiveLink function
max-nextcloud Mar 18, 2024
d956fdd
fix(links): also update if active was unset
max-nextcloud Mar 18, 2024
e02c5ad
refactor(links): operate on state with `linkNodeFromSelection`
max-nextcloud Mar 18, 2024
c1e4bd8
refactor(links): move linkNodeFromSelection into helper
max-nextcloud Mar 18, 2024
96097aa
refactor(links): handle selection changes in plugin
max-nextcloud Mar 18, 2024
4a3165b
refactor(links): use hideLinkBubble command for esc
max-nextcloud Mar 19, 2024
ed923f4
fix(links): simplify updateTooltip and handle active null
max-nextcloud Mar 19, 2024
4bc7586
fix(links): remove special handling for clicked and focus
max-nextcloud Mar 19, 2024
7fe10f9
fix(links): handle esc in DOMEvents rather than prop
max-nextcloud Mar 19, 2024
d79b67c
fix(links) handle linkNodeFromSelection returning false
max-nextcloud Mar 19, 2024
4f5277e
enh(links): close link bubble when opening preview toggle
max-nextcloud Mar 19, 2024
d582f38
fix(lint): add missing jsdoc comments to new files
max-nextcloud Mar 19, 2024
322a752
docs(preview): improve jsdocs for markdownit preview plugin
max-nextcloud Mar 19, 2024
8e78cb7
refactor(links): only get mark from selection
max-nextcloud Mar 20, 2024
385b246
refactor(links): get active link from selection
max-nextcloud Mar 20, 2024
c2fd1e5
fix(links): show bubble with cursor on the end of the link
max-nextcloud Mar 20, 2024
fb0a8ce
tests(links): ignore link bubble when reading link text
max-nextcloud Mar 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(links): move click handling into link bubble plugin
Signed-off-by: Max <max@nextcloud.com>
  • Loading branch information
max-nextcloud committed Mar 20, 2024
commit 90b99beb2e2cc922c7df1e6dcd452fcac5d1a086
27 changes: 17 additions & 10 deletions src/plugins/links.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function linkBubble(pluginKey, options) {
state: {
init: () => ({ clicked: null }),
apply: (tr, cur) => {
const meta = tr.getMeta(linkClickingKey)
const meta = tr.getMeta(linkBubblePlugin)
if (meta?.clicked) {
return { clicked: meta.clicked }
} else {
Expand All @@ -42,29 +42,36 @@ export function linkBubble(pluginKey, options) {
options,
plugin: linkBubblePlugin,
}),
})
return linkBubblePlugin
}

export const linkClickingKey = new PluginKey('textHandleClickLink')
export function linkClicking() {
return new Plugin({
key: linkClickingKey,
props: {
// Required for read-only mode on Firefox.
// For some reason, editor selection doesn't get updated
// when clicking a link in read-only mode on Firefox.
handleClickOn: (view, pos, node, nodePos, event, direct) => {
// Only regard left clicks without Ctrl/Meta
if (event.button !== 0 || event.ctrlKey || event.metaKey) {
if (!direct
|| event.button !== 0
|| event.ctrlKey
|| event.metaKey) {
return false
}
const { dispatch, state } = view
const resolved = state.doc.resolve(pos)
const nodeStart = resolved.pos - resolved.textOffset
const clicked = { pos, resolved, nodePos, event, direct, nodeStart }
dispatch(state.tr.setMeta(linkClickingKey, { clicked }))
dispatch(state.tr.setMeta(linkBubblePlugin, { clicked }))
},
},

})
return linkBubblePlugin
}

export const linkClickingKey = new PluginKey('textHandleClickLink')
export function linkClicking() {
return new Plugin({
key: linkClickingKey,
props: {
handleDOMEvents: {
// Open link in new tab on middle click
auxclick: (view, event) => {
Expand Down
57 changes: 57 additions & 0 deletions src/tests/plugins/linkBubble.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @copyright Copyright (c) 2024 Max <max@nextcloud.com>
*
* @author Max <max@nextcloud.com>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

import { linkBubble } from '../../plugins/links.js'
import { Plugin, EditorState } from '@tiptap/pm/state'
import { schema } from '@tiptap/pm/schema-basic'

describe('linkBubble prosemirror plugin', () => {

test('signature', () => {
expect(linkBubble).toBeInstanceOf(Function)
expect(new linkBubble('key')).toBeInstanceOf(Plugin)
})

test('usage as plugin', () => {
const plugin = new linkBubble('linkBubble')
const state = createState({ plugins: [ plugin ] })
expect(state.plugins).toContain(plugin)
expect(plugin.getState(state)).toEqual({"clicked": null})
})

test('updates plugin state clicked on transaction', () => {
const plugin = new linkBubble('linkBubble')
const state = createState({ plugins: [ plugin ] })
const dummy = { was: 'clicked' }
const tr = state.tr.setMeta(plugin, { clicked: dummy })
const after = state.apply(tr)
expect(plugin.getState(after)).toEqual({"clicked": dummy})
})

})

function createState(options = {}) {
return EditorState.create({
schema,
...options,
})
}