Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Documentation]: Display figcaption without HTML tags #4020

Open
GodsonAddy opened this issue May 6, 2023 · 1 comment
Open

[Documentation]: Display figcaption without HTML tags #4020

GodsonAddy opened this issue May 6, 2023 · 1 comment
Labels
Category: Open Source The issue or pull reuqest is related to the open source packages of Tiptap. Triage: Open A new issue or pullrequest that requires triage (added by default) Type: Documentation The issue or pullrequest is related to documentation

Comments

@GodsonAddy
Copy link

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 editor

Anything to add? (optional)

This a link to the codesandbox: https://codesandbox.io/p/sandbox/polished-wildflower-98rju0

@GodsonAddy GodsonAddy added Category: Open Source The issue or pull reuqest is related to the open source packages of Tiptap. Triage: Open A new issue or pullrequest that requires triage (added by default) Type: Documentation The issue or pullrequest is related to documentation labels May 6, 2023
@GodsonAddy
Copy link
Author

GodsonAddy commented Nov 3, 2023

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();
    }
  };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Category: Open Source The issue or pull reuqest is related to the open source packages of Tiptap. Triage: Open A new issue or pullrequest that requires triage (added by default) Type: Documentation The issue or pullrequest is related to documentation
Projects
Status: Triage open
Development

No branches or pull requests

3 participants