Hi, I made the node view but when I click on it, it’s not chosen. I have a typical image
node and my custom figure
node whose content is image paragraph?
. The views are the following:
// Image
<NodeViewWrapper
as="div"
className="image-node-view group"
contentEditable={false}
>
<img
contentEditable={false}
src={node.attrs.src}
ref={resizableImgRef as any}
className="rounded-lg"
alt={node.attrs.src}
width={isWidthInPercentages ? "100%" : node.attrs.width}
height={!isWidthInPercentages ? node.attrs.height : null}
style={style}
/>
<div
contentEditable={false}
className="horizontal-resize-handle group-hover:bg-black group-hover:border-2 group-hover:border-white"
title="Resize"
onClick={({ clientX }) => setLastClientX(clientX)}
onMouseDown={startHorizontalResize}
onMouseUp={stopHorizontalResize}
/>
</NodeViewWrapper>
// Figure
return (
<NodeViewWrapper
as="div"
className={
"figure-node-view " +
(isFloat
? "f-" + node.attrs.dataFloat
: isAlign
? "justify-" + node.attrs.dataAlign
: "")
}
contentEditable={false}
style={{
width: isWidthInPercentages ? node.attrs.width : undefined,
marginRight:
(isWidthInPercentages && node.attrs.dataAlign === "left") ||
node.attrs.dataAlign === "center"
? "auto"
: undefined,
marginLeft:
(isWidthInPercentages && node.attrs.dataAlign === "right") ||
node.attrs.dataAlign === "center"
? "auto"
: undefined,
}}
>
<NodeViewContent/>
</NodeViewWrapper>
Also, here is my figure
node:
declare module "@tiptap/core" {
interface Commands<ReturnType> {
figure: {
/**
* Set media
*/
setFigure: (options: {
src?: string;
alt?: string;
title?: string;
width?: string;
height?: string;
dataAlign?: string;
dataFloat?: string;
caption?: boolean;
}) => ReturnType;
/**
* Toggle caption
*/
toggleCaption: () => ReturnType;
/**
* Rotate
*/
rotate: (deg: number, mode: "" | "-x" | "-y") => ReturnType;
};
}
}
export interface FigureOptions {
HTMLAttributes: Record<string, any>;
}
export const Figure = Node.create<FigureOptions>({
name: "figure",
addOptions() {
return {
inline: false,
allowBase64: false,
HTMLAttributes: {},
};
},
inline: false,
group: "block",
draggable: true,
content: "image paragraph?",
selectable: true,
addAttributes() {
return {
src: {
default: null,
renderHTML: (attributes) => ({
src: attributes.src,
}),
parseHTML: (element: HTMLImageElement) => element.src,
},
alt: {
default: null,
parseHTML: (element: HTMLElement) => element.getAttribute("alt"),
},
title: {
default: null,
parseHTML: (element: HTMLElement) => element.getAttribute("title"),
},
width: {
default: "75%",
parseHTML: (element: HTMLElement) => element.getAttribute("width"),
},
height: {
default: "auto",
parseHTML: (element: HTMLElement) => element.getAttribute("height"),
},
dataAlign: {
default: "center", // 'left' | 'center' | 'right'
parseHTML: (element: HTMLElement) => element.getAttribute("data-align"),
},
dataFloat: {
default: null, // 'left' | 'right'
parseHTML: (element: HTMLElement) => element.getAttribute("data-float"),
},
caption: {
default: false,
parseHTML: (element: HTMLElement) =>
element.getAttribute("data-caption"),
},
};
},
addCommands() {
return {
setFigure:
(options) =>
({ chain }) => {
return chain()
.insertContent({
type: this.name,
attrs: options,
content: [
{ type: "image", attrs: options },
{
type: "paragraph",
content: [
{
type: "text",
text: "Caption",
},
],
},
],
})
.run();
},
toggleCaption:
() =>
({ editor }) => {
let { caption } = editor.getAttributes(this.name);
return editor.commands.updateAttributes("caption", {
caption: !caption,
});
// let { caption } = editor.getAttributes("resizableMediaWithCaption");
// if (caption) {
// editor
// .chain()
// .focus()
// .deleteNode("caption")
// .updateAttributes("resizableMediaWithCaption", {
// caption: !caption,
// })
// .run();
// } else {
// // editor.commands.insertContent({
// // type: "caption",
// // content: [
// // {
// // type: "paragraph",
// // content: [
// // {
// // type: "text",
// // text: "Caption",
// // },
// // ],
// // },
// // ],
// // });
// return true;
// }
},
rotate:
(deg, mode) =>
({ commands, editor }) => {
let attr: string = `data-rotate${mode}`;
let currDeg = editor.getAttributes(this.name)[attr];
currDeg = currDeg ?? 0;
let attrs: Record<string, any> = {};
attrs[attr] = currDeg + deg;
return commands.updateAttributes(this.name, attrs);
},
};
},
parseHTML() {
return [
{
tag: "figure",
},
];
},
renderHTML({ HTMLAttributes }) {
return [
"figure",
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
0,
];
},
addNodeView() {
return ReactNodeViewRenderer(ImageNodeView);
},
When I insert simple Image View (it’s resizable), then when I click from paragraph on that image, then the cursor in paragraph correctly disappear (but unfortunately ProseMirror-selectedNode
class is not added to the view which is the first, less important problem) but when I insert Figure node, then the cursor doesnt disappear from paragraph. What do you think? I’m asking because I want to show BubbleMenu when figure is selected but it’s currently not possible to select it.
EDIT I solved my main problem. So the remaining question is why selectedNode
class is not added to the node when I click it? The only thing that is changing is that ProseMirror-hideselection
class is added to the main editor container. Why?