81 lines
3.0 KiB
TypeScript
81 lines
3.0 KiB
TypeScript
import Image from '@tiptap/extension-image'
|
|
import { mergeAttributes } from '@tiptap/core'
|
|
import { ReactNodeViewRenderer } from '@tiptap/react'
|
|
import { ImageNodeView } from './ImageNodeView'
|
|
|
|
/**
|
|
* تصویر مقاله با کپشن.
|
|
* خروجی HTML در سایت: <figure><img><figcaption>کپشن</figcaption></figure>
|
|
* و در ویرایشگر با NodeView سفارشی (ورودی کپشن + دکمه حذف) نمایش داده میشود.
|
|
*/
|
|
export const ArticleImage = Image.extend({
|
|
addAttributes() {
|
|
return {
|
|
// ویژگیهای استاندارد تصویر (src/alt/title/width/height) از افزونه پایه
|
|
...this.parent?.(),
|
|
caption: {
|
|
default: '',
|
|
parseHTML: (element) =>
|
|
element.closest('figure')?.querySelector('figcaption')?.textContent?.trim() ?? '',
|
|
},
|
|
// پیوند کپشن — کل متن figcaption به این نشانی لینک میشود
|
|
caption_href: {
|
|
default: '',
|
|
parseHTML: (element) =>
|
|
element.closest('figure')?.querySelector('figcaption a')?.getAttribute('href') ?? '',
|
|
},
|
|
}
|
|
},
|
|
|
|
parseHTML() {
|
|
return [
|
|
{
|
|
tag: 'figure',
|
|
priority: 60,
|
|
getAttrs: (element) => {
|
|
if (!(element instanceof HTMLElement)) return false
|
|
const img = element.querySelector('img')
|
|
if (!img) return false
|
|
return {
|
|
src: img.getAttribute('src'),
|
|
alt: img.getAttribute('alt'),
|
|
title: img.getAttribute('title'),
|
|
caption: element.querySelector('figcaption')?.textContent?.trim() ?? '',
|
|
caption_href: element.querySelector('figcaption a')?.getAttribute('href') ?? '',
|
|
}
|
|
},
|
|
},
|
|
// تصاویر بدون figure (محتوای قدیمی یا چسباندهشده)
|
|
{ tag: 'img[src]:not([src^="data:"])' },
|
|
]
|
|
},
|
|
|
|
renderHTML({ HTMLAttributes }) {
|
|
const { caption, ...imageAttributes } = HTMLAttributes
|
|
const image: [string, Record<string, unknown>] = [
|
|
'img',
|
|
mergeAttributes(this.options.HTMLAttributes, imageAttributes),
|
|
]
|
|
|
|
// کپشن خالی → عنصر figcaption اصلاً رندر نمیشود (فقط تصویر میماند)
|
|
const captionHref = (HTMLAttributes.caption_href as string | undefined)?.trim() ?? ''
|
|
// توجه: آرایهٔ درونی بهصورت تو در تو پاس میشود — اسپرد آن اسپک را تخت میکند
|
|
// و رندرر استاتیک با «Unsupported DomOutputSpec» میمیرد.
|
|
const captionSpec = captionHref
|
|
? ['a', { href: captionHref, target: '_blank', rel: 'noopener noreferrer nofollow' }, caption]
|
|
: caption
|
|
return caption
|
|
? [
|
|
'figure',
|
|
{ class: 'article-figure' },
|
|
image,
|
|
['figcaption', { class: 'article-figure__caption' }, captionSpec],
|
|
]
|
|
: ['figure', { class: 'article-figure' }, image]
|
|
},
|
|
|
|
addNodeView() {
|
|
return ReactNodeViewRenderer(ImageNodeView)
|
|
},
|
|
})
|