diff --git a/packages/editor/src/extensions/image/BubbleItemImageHref.vue b/packages/editor/src/extensions/image/BubbleItemImageHref.vue
new file mode 100644
index 0000000..ce074a8
--- /dev/null
+++ b/packages/editor/src/extensions/image/BubbleItemImageHref.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
diff --git a/packages/editor/src/extensions/image/ImageView.vue b/packages/editor/src/extensions/image/ImageView.vue
index 249103a..5ba6c21 100644
--- a/packages/editor/src/extensions/image/ImageView.vue
+++ b/packages/editor/src/extensions/image/ImageView.vue
@@ -38,6 +38,14 @@ const alt = computed({
},
});
+const href = computed({
+ get: () => {
+ return props.node?.attrs.href;
+ },
+ set: (href: string) => {
+ props.updateAttributes({ href: href });
+ },
+});
function handleSetFocus() {
props.editor.commands.setNodeSelection(props.getPos());
}
@@ -93,6 +101,7 @@ onMounted(() => {
:src="src"
:title="node.attrs.title"
:alt="alt"
+ :href="href"
class="w-full h-full"
/>
diff --git a/packages/editor/src/extensions/image/index.ts b/packages/editor/src/extensions/image/index.ts
index 116ecab..fcc580f 100644
--- a/packages/editor/src/extensions/image/index.ts
+++ b/packages/editor/src/extensions/image/index.ts
@@ -1,5 +1,5 @@
import TiptapImage from "@tiptap/extension-image";
-import { isActive, type Editor } from "@tiptap/core";
+import { isActive, mergeAttributes, type Editor } from "@tiptap/core";
import { VueNodeViewRenderer } from "@tiptap/vue-3";
import ImageView from "./ImageView.vue";
import type { ImageOptions } from "@tiptap/extension-image";
@@ -12,6 +12,7 @@ import type { EditorState } from "@tiptap/pm/state";
import BubbleItemImageSize from "./BubbleItemImageSize.vue";
import BubbleItemImageAlt from "./BubbleItemImageAlt.vue";
import BubbleItemVideoLink from "./BubbleItemImageLink.vue";
+import BubbleItemImageHref from "./BubbleItemImageHref.vue";
import { BlockActionSeparator } from "@/components";
import MdiFormatAlignLeft from "~icons/mdi/format-align-left";
import MdiFormatAlignCenter from "~icons/mdi/format-align-center";
@@ -22,6 +23,7 @@ import MdiShare from "~icons/mdi/share";
import MdiTextBoxEditOutline from "~icons/mdi/text-box-edit-outline";
import MdiDeleteForeverOutline from "~icons/mdi/delete-forever-outline?color=red";
import { deleteNode } from "@/utils";
+import MdiLink from "~icons/mdi/link";
const Image = TiptapImage.extend({
inline() {
@@ -61,6 +63,18 @@ const Image = TiptapImage.extend({
};
},
},
+ href: {
+ default: null,
+ parseHTML: (element) => {
+ const href = element.getAttribute("href") || null;
+ return href;
+ },
+ renderHTML: (attributes) => {
+ return {
+ href: attributes.href,
+ };
+ },
+ },
style: {
renderHTML() {
return {
@@ -84,7 +98,6 @@ const Image = TiptapImage.extend({
},
];
},
-
addOptions() {
return {
...this.parent?.(),
@@ -186,10 +199,20 @@ const Image = TiptapImage.extend({
},
{
priority: 100,
- component: markRaw(BlockActionSeparator),
+ props: {
+ icon: markRaw(MdiLink),
+ title: i18n.global.t("editor.extensions.image.edit_href"),
+ action: () => {
+ return markRaw(BubbleItemImageHref);
+ },
+ },
},
{
priority: 110,
+ component: markRaw(BlockActionSeparator),
+ },
+ {
+ priority: 120,
props: {
icon: markRaw(MdiDeleteForeverOutline),
title: i18n.global.t("editor.common.button.delete"),
@@ -201,6 +224,16 @@ const Image = TiptapImage.extend({
},
};
},
+ renderHTML({ HTMLAttributes }) {
+ if (HTMLAttributes.href) {
+ return [
+ "a",
+ { href: HTMLAttributes.href },
+ ["img", mergeAttributes(HTMLAttributes)],
+ ];
+ }
+ return ["img", mergeAttributes(HTMLAttributes)];
+ },
});
const handleSetTextAlign = (
diff --git a/packages/editor/src/locales/en.yaml b/packages/editor/src/locales/en.yaml
index 9869358..34ea7a5 100644
--- a/packages/editor/src/locales/en.yaml
+++ b/packages/editor/src/locales/en.yaml
@@ -52,6 +52,7 @@ editor:
restore_size: Restore original size
edit_link: Edit link
edit_alt: Edit alt
+ edit_href: Edit the image hyperlink
video:
disable_controls: Hide controls
enable_controls: Show controls
@@ -107,6 +108,7 @@ editor:
placeholder:
link_input: Enter the link and press enter to confirm.
alt_input: Enter the image alt and press enter to confirm.
+ alt_href: Enter the image hyperlink and press enter to confirm.
button:
new_line: New line
delete: Delete
diff --git a/packages/editor/src/locales/zh-CN.yaml b/packages/editor/src/locales/zh-CN.yaml
index 6103894..5146bdc 100644
--- a/packages/editor/src/locales/zh-CN.yaml
+++ b/packages/editor/src/locales/zh-CN.yaml
@@ -52,6 +52,7 @@ editor:
restore_size: 恢复原始尺寸
edit_link: 修改链接
edit_alt: 修改图片 alt 属性
+ edit_href: 修改图片跳转链接
video:
disable_controls: 隐藏控制面板
enable_controls: 显示控制面板
@@ -107,6 +108,7 @@ editor:
placeholder:
link_input: 输入链接,按回车确定
alt_input: 输入图片 alt 属性值,按回车确认
+ alt_href: 输入图片跳转链接,按回车确认
button:
new_line: 换行
delete: 删除