amirhhashemi / tiptap-text-direction

Text direction extension for Tiptap
https://www.npmjs.com/package/tiptap-text-direction
MIT License
38 stars 1 forks source link

How to apply in span tag? #12

Closed ngekoding closed 1 year ago

ngekoding commented 1 year ago

Hi, thanks for this awesome extension. Can you help how to apply the dir in span tag?

So, I have a content like <span class="text-arabic">...</span> and I want to add the dir="rtl" to it. Currently it will wrapped with a <p> tag, so it make:

<p dir="rtl">
  <span class="text-arabic">...</span>
</p>

I want make it:

<span class="text-arabic" dir="rtl">...</span>

Thanks in advance.

amirhhashemi commented 1 year ago

You don't need this extension to detect the direction since you already know the direction of the spans. You can create a custom node that selects the span tags that have text-arabic class and add dir="rtl" directly to them:

import { mergeAttributes, Node } from '@tiptap/core'

const ArabicNode = Node.create({
  name: "arabic", // a unique name

  // You might want to give it a high priority of you have other nodes that select `span` tags
  // priority: 1000,

  inline: true,

  group: "inline",

  content: "inline*",

  parseHTML() {
    return [
      {
        tag: "span",
        getAttrs: (node) => node.classList.contains("text-arabic") && null,
      },
    ];
  },

  renderHTML({ HTMLAttributes }) {
    return ["span", mergeAttributes({ dir: "rtl", class: "text-arabic" }, HTMLAttributes), 0];
  },
});

// Dont' forget to register it
const editor = new Editor({
  extensions: [
    ArabicNode,
    // …
  ],
})

I haven't tested the above code, but you get the idea.

If you're not sure about the direction and want this extension to detect it, you can add the name of the new node ("arabic" in this case) to the types option:

TextDirection.configure({
  types: ["heading", "paragraph", "arabic"],
});

In this case you should remove renderHTML from the node since the extension doesn't add the dir attribute if the node already have one.

Let me know if it works for you

amirhhashemi commented 1 year ago

I forgot to add inline: true.

I updated the code

amirhhashemi commented 1 year ago

You should also render class="arabic-text" manually otherwise it would be removed from the element. I updated the code again

ngekoding commented 1 year ago

I was try it and get nothing, the dir attribute not added.

ngekoding commented 1 year ago

I want to implement it to this project https://github.com/ngekoding/notes-to-image, so currently I use custom extension to add the text-arabic class.

ngekoding commented 1 year ago

Can you give me an official extension to follow (suggestion to learn) for this?

amirhhashemi commented 1 year ago

It's wired because it works for me. Did you add ArabicNode to the list of extensions?

Can you show me your extension code so I can help you?

If you want to learn more about it you can follow the official docs: https://tiptap.dev/api/nodes

I don't know an official extension that uses this technique but almost all extensions has a custom node that you can use as a reference. https://github.com/ueberdosis/tiptap/tree/develop/packages

ngekoding commented 1 year ago

Thanks for the references, I will try it again and update the info here.

I just copy your code above for the extension.

amirhhashemi commented 1 year ago

Did you add the node to the list of extension? for example in React:

const editor = new Editor({
  extensions: [
    ArabicNode,
    // …
  ],
})
amirhhashemi commented 1 year ago

How are you adding the "text-arabic" class to the span? Without that class the code I wrote wouldn't work

ngekoding commented 1 year ago

Did you add the node to the list of extension? for example in React:

const editor = new Editor({
  extensions: [
    ArabicNode,
    // …
  ],
})

Yes, already added it.

ngekoding commented 1 year ago

How are you adding the "text-arabic" class to the span? Without that class the code I wrote wouldn't work

I use a custom extension, here is the extension I use https://github.com/ngekoding/notes-to-image/blob/master/src/tiptap-extensions/text-class/text-class.js

If you have a time, you can also check the app directly to show it in action.

amirhhashemi commented 1 year ago

Just update these two function in you extension and it should work:

  parseHTML() {
    return [
      {
        tag: 'span',
        getAttrs: (node) => node.classList.contains("text-arabic") && null,
      },
    ];
  },

  renderHTML({ HTMLAttributes }) {
    return ["span", mergeAttributes({ dir: "rtl", class: "text-arabic" }, HTMLAttributes), 0];
  },

And delete my custom node since you already have one.

ngekoding commented 1 year ago

You mean I just use the text-class extension to also include the dir attribute?

amirhhashemi commented 1 year ago

Yes

ngekoding commented 1 year ago

MashaAllah, it works! and the text-arabic typed twice.

Arabic Test

But, how to make it in different extension?

amirhhashemi commented 1 year ago

I think the custom node I wrote conflicts with your extension and therefor it doesn't work. Probably giving a higher priority fixes that:

const ArabicNode = Node.create({
  priority: 1000,
  // ...
})

But since you already have an extension that selects spans it's easier to add dir directly with that.

I think the reason that "text-arabic" class is added twice is that you are adding it twice, once in renderHTML of the class attribute and once in the global renderHTML. Removing it from the global renderHTML should fix that:

  renderHTML({ HTMLAttributes }) {
    return ["span", mergeAttributes({ dir: "rtl" }, HTMLAttributes), 0];
  },

Also, if you are using the text-class extension for different purposes (maybe different classes do different things) you can do a little check before adding the dir attribute:

  // Select all `span` tags regardless of the class
  parseHTML() {
    return [
      {
        tag: 'span',
      },
    ];
  },
  // If it had the "text-arabic" class add the `dir` attribute, otherwise just render the `span`
  renderHTML({ HTMLAttributes }) {
    if (HTMLAttributes.class.includes("text-arabic")) {
      return ["span", mergeAttributes({ dir: "rtl" }, HTMLAttributes), 0];
    }
    return ["span", HTMLAttributes, 0];
  },
ngekoding commented 1 year ago

I think the custom node I wrote conflicts with your extension and therefor it doesn't work. Probably giving a higher priority fixes that:

const ArabicNode = Node.create({
  priority: 1000,
  // ...
})

At the first of try I was already use the priority with no works.

Also, if you are using the text-class extension for different purposes (maybe different classes do different things) you can do a little check before adding the dir attribute:

I think it is better to make a separate extension for different purpose, I will try it again.

amirhhashemi commented 1 year ago

You extension is actually a Mark, maybe my extension should be a Mark too.

const ArabicMark = Mark.create({ /* ... */ })
amirhhashemi commented 1 year ago

You can get more help in the TipTap discord. My extension works but somehow it conflicts with your extension. The problem is identified so probably a more experienced TipTap user knows the solution

ngekoding commented 1 year ago

You extension is actually a Mark, maybe my extension should be a Mark too.

const ArabicMark = Mark.create({ /* ... */ })

You are right, it works now but...

But I need to refresh the web page to get the updated content with dir="rtl", so how can I get it automatically every time I add the text-arabic class?

amirhhashemi commented 1 year ago

It seems like a bug. dir="rtl" should be added to all nodes that have text-arabic class. You shouldn't need to refresh the page.

I would say the two extensions are still somehow conflicting with each other. Otherwise whether it's a bug or something beyond my knowledge.

My suggestion would be to render the dir attribute in the text-class extension.

It's a good idea to discuss this in the TipTap Discord channel. There are a lot of knowledgeable people there.

ngekoding commented 1 year ago

So finally I make it as you suggest, here is the code https://github.com/ngekoding/notes-to-image/commit/3423fb4a2c18c2807d6a4a36a1734e17fe32b360

Thank you very much