pref: optimize the rich text editor link paste logic (#5680)

#### What type of PR is this?

/kind improvement
/area editor
/area ui
/milestone 2.15.x

#### What this PR does / why we need it:

优化默认富文本编辑器中自动创建链接的逻辑。

- 移除了粘贴文本时,如果文本为链接则会自动转为链接的问题。
- 移除输入链接文本之后回车,会自动转化为链接的问题。
- 新增当选中的文本内容为链接时,点击链接按钮,将会自动转化选中的文本为链接。

#### How to test it?

在富文本编辑器中,测试如下场景:

1. 在浏览器地址栏复制一个链接,粘贴后不会再转为链接,而是一个普通文本。
2. 输入一段链接文本,按回车后是否不会再被转为链接。
3. 选中一段可以被解析为链接的地址,选中此地址,点击链接按钮,此地址是否会被默认转为链接。

#### Which issue(s) this PR fixes:

Fixes #5653 

#### Does this PR introduce a user-facing change?
```release-note
优化默认富文本编辑器中文本自动转为链接的相关逻辑
```
pull/5698/head
Takagi 2024-04-13 08:50:08 +08:00 committed by GitHub
parent 37f530b619
commit 505f38a145
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 9 deletions

View File

@ -82,12 +82,14 @@
"floating-vue": "2.0.0-beta.24",
"github-markdown-css": "^5.2.0",
"highlight.js": "11.8.0",
"linkifyjs": "^4.1.3",
"lowlight": "^3.0.0",
"scroll-into-view-if-needed": "^3.1.0",
"tippy.js": "^6.3.7"
},
"devDependencies": {
"@iconify/json": "^2.2.117",
"@types/linkifyjs": "^2.1.7",
"release-it": "^16.1.5",
"vite-plugin-dts": "^3.7.3"
},

View File

@ -3,7 +3,9 @@ import { computed, type Component } from "vue";
import { VTooltip, Dropdown as VDropdown } from "floating-vue";
import MdiLinkVariant from "~icons/mdi/link-variant";
import { i18n } from "@/locales";
import type { Editor } from "@/tiptap/vue-3";
import { type Editor } from "@/tiptap/vue-3";
import { test } from "linkifyjs";
import { TextSelection } from "@tiptap/pm/state";
const props = defineProps<{
editor: Editor;
@ -39,10 +41,44 @@ const target = computed({
});
},
});
/**
* Convert the currently selected text when clicking the link
*/
const handleLinkBubbleButton = () => {
if (props.isActive({ editor: props.editor })) {
return;
}
const { state } = props.editor;
const { selection } = state;
const { empty } = selection;
if (selection instanceof TextSelection) {
if (empty) {
return false;
}
const { content } = selection.content();
if (!content || content.childCount !== 1) {
return false;
}
const text = content.firstChild?.textContent;
if (text && test(text, "url")) {
props.editor.commands.setLink({
href: text,
target: "_self",
});
}
}
};
</script>
<template>
<VDropdown class="inline-flex" :triggers="['click']" :distance="10">
<VDropdown
class="inline-flex"
:triggers="['click']"
:distance="10"
@click="handleLinkBubbleButton"
>
<button
v-tooltip="
isActive({ editor })

View File

@ -15,6 +15,11 @@ const Link = TiptapLink.extend<ExtensionOptions & LinkOptions>({
};
},
addPasteRules() {
// Remove the function of pasted text parsing as a link
return [];
},
renderHTML({ HTMLAttributes }) {
const href = HTMLAttributes.href;
// False positive; we're explicitly checking for javascript: links to ignore them

View File

@ -568,6 +568,9 @@ importers:
highlight.js:
specifier: 11.8.0
version: 11.8.0
linkifyjs:
specifier: ^4.1.3
version: 4.1.3
lowlight:
specifier: ^3.0.0
version: 3.0.0
@ -584,6 +587,9 @@ importers:
'@iconify/json':
specifier: ^2.2.117
version: 2.2.147
'@types/linkifyjs':
specifier: ^2.1.7
version: 2.1.7
release-it:
specifier: ^16.1.5
version: 16.2.1(typescript@5.3.3)
@ -5621,7 +5627,7 @@ packages:
ts-dedent: 2.2.0
type-fest: 2.19.0
vue: 3.4.19(typescript@5.3.3)
vue-component-type-helpers: 2.0.7
vue-component-type-helpers: 2.0.11
transitivePeerDependencies:
- encoding
- supports-color
@ -5919,7 +5925,7 @@ packages:
dependencies:
'@tiptap/core': 2.2.3(@tiptap/pm@2.2.3)
'@tiptap/pm': 2.2.3
linkifyjs: 4.1.1
linkifyjs: 4.1.3
dev: false
/@tiptap/extension-list-item@2.2.3(@tiptap/core@2.2.3):
@ -6321,6 +6327,12 @@ packages:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true
/@types/linkifyjs@2.1.7:
resolution: {integrity: sha512-+SIYXs1lajyD7t/2+V9GLfdFlc/6Nr2tr65kjA2F5oOzBlPH+NiPqySJDHzREoGcL91Au9Qef8M5JdZiRXsaJw==}
dependencies:
'@types/react': 18.2.41
dev: true
/@types/lodash-es@4.17.12:
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
dependencies:
@ -8577,6 +8589,7 @@ packages:
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
requiresBuild: true
dev: true
/commander@4.1.1:
@ -12623,8 +12636,8 @@ packages:
uc.micro: 2.0.0
dev: false
/linkifyjs@4.1.1:
resolution: {integrity: sha512-zFN/CTVmbcVef+WaDXT63dNzzkfRBKT1j464NJQkV7iSgJU0sLBus9W0HBwnXK13/hf168pbrx/V/bjEHOXNHA==}
/linkifyjs@4.1.3:
resolution: {integrity: sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==}
dev: false
/lint-staged@13.2.2:
@ -17591,8 +17604,8 @@ packages:
resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
dev: true
/vue-component-type-helpers@2.0.7:
resolution: {integrity: sha512-7e12Evdll7JcTIocojgnCgwocX4WzIYStGClBQ+QuWPinZo/vQolv2EMq4a3lg16TKfwWafLimG77bxb56UauA==}
/vue-component-type-helpers@2.0.11:
resolution: {integrity: sha512-8aluKz5oVC8PvVQAYgyIefOlqzKVmAOTCx2imbrFBVLbF7mnJvyMsE2A7rqX/4f4uT6ee9o8u3GcoRpUWc0xsw==}
dev: true
/vue-demi@0.13.11(vue@3.4.19):

View File

@ -243,7 +243,7 @@ onMounted(() => {
}),
ExtensionTaskList,
ExtensionLink.configure({
autolink: true,
autolink: false,
openOnClick: false,
}),
ExtensionTextAlign.configure({