import React, { useCallback } from 'react' import { Node, mergeAttributes as mergeNodeAttributes } from '@tiptap/core' import { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react' function readImageAttrs(element) { const imageElements = Array.from(element.querySelectorAll?.('img') || []) const subtitleElement = element.querySelector?.('figcaption') return { leftSrc: imageElements[0]?.getAttribute('src') || '', leftAlt: imageElements[0]?.getAttribute('alt') || '', rightSrc: imageElements[1]?.getAttribute('src') || '', rightAlt: imageElements[1]?.getAttribute('alt') || '', subtitle: subtitleElement?.textContent?.trim() || '', } } function RichCompareNodeView({ editor, node, selected, updateAttributes, deleteNode, getPos }) { const selectNode = useCallback(() => { if (!editor || typeof getPos !== 'function') { return } editor.chain().focus().setNodeSelection(getPos()).run() }, [editor, getPos]) return ( { if (event.target instanceof HTMLElement && event.target.closest('input, textarea, button, select, label')) { return } selectNode() }} >
Left {node.attrs.leftAlt
Right {node.attrs.rightAlt
{!selected && node.attrs.subtitle ? (
{node.attrs.subtitle}
) : null} {selected ? (
) : null}
) } const RichCompare = Node.create({ name: 'imageCompare', group: 'block', atom: true, addAttributes() { return { leftSrc: { default: '' }, leftAlt: { default: '' }, rightSrc: { default: '' }, rightAlt: { default: '' }, subtitle: { default: '' }, } }, parseHTML() { return [ { tag: 'figure[data-rich-compare]', getAttrs: (element) => readImageAttrs(element), }, ] }, renderHTML({ node, HTMLAttributes }) { const { leftSrc: _leftSrc, leftAlt: _leftAlt, rightSrc: _rightSrc, rightAlt: _rightAlt, subtitle: _subtitle, ...figureHTMLAttributes } = HTMLAttributes const leftImageAttributes = { src: node.attrs.leftSrc, alt: node.attrs.leftAlt || '', loading: 'lazy', decoding: 'async', class: 'rich-compare-node__img', } const rightImageAttributes = { src: node.attrs.rightSrc, alt: node.attrs.rightAlt || '', loading: 'lazy', decoding: 'async', class: 'rich-compare-node__img', } return [ 'figure', mergeNodeAttributes(this.options.HTMLAttributes, figureHTMLAttributes, { 'data-rich-compare': 'true', }), ['div', { class: 'rich-compare-node__grid' }, ['div', { class: 'rich-compare-node__tile' }, ['img', leftImageAttributes]], ['div', { class: 'rich-compare-node__tile' }, ['img', rightImageAttributes]], ], ...(node.attrs.subtitle ? [['figcaption', { class: 'rich-compare-node__subtitle' }, node.attrs.subtitle]] : []), ] }, addNodeView() { return ReactNodeViewRenderer(RichCompareNodeView) }, }) export default RichCompare