import Image from '@tiptap/extension-image' import { mergeAttributes } from '@tiptap/core' import { ReactNodeViewRenderer } from '@tiptap/react' import { ImageNodeView } from './ImageNodeView' /** * تصویر مقاله با کپشن. * خروجی HTML در سایت:
کپشن
* و در ویرایشگر با 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] = [ '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) }, })