Open GodsonAddy opened 1 year ago
So this is how I was able to solve it
import {
findChildrenInRange,
mergeAttributes,
Node as TiptapNode,
nodeInputRule,
Tracker,
} from "@tiptap/core";
import styles from "../../../../styles/stories.module.css";
const inputRegex = /!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\)/;
export const Figure = TiptapNode.create({
name: "figure",
addOptions() {
return {
HTMLAttributes: {},
};
},
group: "block",
content: "inline*",
draggable: true,
isolating: false,
selectable: true,
addAttributes() {
return {
src: {
default: null,
parseHTML: (element) =>
element.querySelector("img")?.getAttribute("src"),
},
alt: {
default: null,
parseHTML: (element) =>
element.querySelector("img")?.getAttribute("alt"),
},
title: {
default: null,
parseHTML: (element) =>
element.querySelector("img")?.getAttribute("title"),
},
};
},
parseHTML() {
return [
{
tag: "figure",
contentElement: "figcaption",
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"figure",
{
...this.options.HTMLAttributes,
},
[
"img",
mergeAttributes(HTMLAttributes, {
draggable: false,
contenteditable: false,
class: styles.my_image,
}),
],
[
"figcaption",
{
"data-placeholder": "Your image caption goes here",
},
0,
],
];
},
addCommands() {
return {
setFigure:
({ caption, ...attrs }) =>
({ chain }) => {
const childNodes = Array.from(caption).map((child, index) => {
if (child.nodeType === Node.TEXT_NODE) {
return {
type: "text",
text: child.textContent,
marks: [],
};
} else if (child.nodeType === Node.ELEMENT_NODE) {
const marks = [];
if (child.href) {
marks.push({
type: "link",
attrs: {
class: child.className,
href: child.href,
target: child.target,
rel: child.rel,
},
});
}
return {
type: "text",
text: child.textContent,
marks,
};
}
});
return (
chain()
.insertContent({
type: this.name,
attrs,
content: caption ? childNodes : [],
})
.command(({ tr, commands }) => {
const { doc, selection } = tr;
const position = doc
.resolve(Math.max(selection.to - 2, 0))
.end();
return commands.insertContentAt(position, {
type: "paragraph",
});
})
.run()
);
},
imageToFigure:
() =>
({ tr, commands }) => {
const { doc, selection } = tr;
const { from, to } = selection;
const images = findChildrenInRange(
doc,
{ from, to },
(node) => node.type.name === "image"
);
if (!images.length) {
return false;
}
const tracker = new Tracker(tr);
return commands.forEach(images, ({ node, pos }) => {
const mapResult = tracker.map(pos);
if (mapResult.deleted) {
return false;
}
const range = {
from: mapResult.position,
to: mapResult.position + node.nodeSize,
};
return commands.insertContentAt(range, {
type: this.name,
attrs: {
src: node.attrs.src,
},
});
});
},
figureToImage:
() =>
({ tr, commands }) => {
const { doc, selection } = tr;
const { from, to } = selection;
const figures = findChildrenInRange(
doc,
{ from, to },
(node) => node.type.name === this.name
);
if (!figures.length) {
return false;
}
const tracker = new Tracker(tr);
return commands.forEach(figures, ({ node, pos }) => {
const mapResult = tracker.map(pos);
if (mapResult.deleted) {
return false;
}
const range = {
from: mapResult.position,
to: mapResult.position + node.nodeSize,
};
return commands.insertContentAt(range, {
type: "image",
attrs: {
src: node.attrs.src,
},
});
});
},
};
},
addInputRules() {
return [
nodeInputRule({
find: inputRegex,
type: this.type,
getAttributes: (match) => {
const [, src, alt, title] = match;
return { src, alt, title };
},
}),
];
},
});
const InsertImage = async (url, newAlt) => {
if (url) {
const parsedCaption = `Photo by <a target="_blank" rel="noopener noreferrer nofollow" class=" " href="https://unsplash.com">name</a> on <a target="_blank" rel="noopener noreferrer nofollow" class="" href="https://unsplash.com">Unsplash</a>`;
const parser = new DOMParser();
const captionNodes = parser.parseFromString(parsedCaption, "text/html" );
const childNodes = captionNodes.body.childNodes;
editor
.chain()
.focus()
.setFigure({ src: url, alt: newAlt, caption: childNodes })
.run();
}
};
What’s the URL to the page you’re sending feedback for?
https://tiptap.dev/experiments/figure
What part of the documentation needs improvement?
I have gone through the documentation of the Figure and I want when a user adds an image , that image gets a default figcaption. This figcaption contains links, bold, italics, etc.
What is helpful about that part?
It's really important for the kind of project I'm building
What is hard to understand, missing or misleading?
Because I want to add a default figcaption that already has links, italics, etc, I need to add HTML tags. But adding this tags, I see the tags in the frontend which shouldn't be so. eg:
<p>Figcaption from <a href="https://tiptap.dev"> @tiptap editor </a> </p>
. The end result should be Figcaption from @tiptap editorAnything to add? (optional)
This a link to the codesandbox: https://codesandbox.io/p/sandbox/polished-wildflower-98rju0