nozer/quill-delta-to-html

How to handle custom attributes?

Closed this issue · 5 comments

I need to wrap text elements with custom blots that has a specific attribute.

The ops:

{
    "ops": [
        {
            "attributes": {
                "spoiler": true
            },
            "insert": "Testing spoiler tags"
        },
        {
            "insert": ". ignore this\n"
        }
    ]
}

What it currently outputs:
<p>Testing spoiler tags. ignore this</p>

What i want it to output
<p><spoiler>Testing spoiler tags</spoiler>. ignore this</p>

I'm also running into trouble regarding this and wondering it there's a bug somewhere. inserts with a type work just fine:

"insert": {
  "customemoji": {
      "text": ":heart:"
   }
}

// Custom converter for custom emoji
if (customOp.insert.type === 'customemoji') {
  const emojiText = customOp.insert.value.text;

  return emojiText;
}

but attributes don't seem to be caught at all:

{
  "insert": "\n",
   "attributes": {
      "horizontalrule": true
    }
},

// Custom converter for Slack horizontal rule
if (customOp. attributes.horizontalrule) {
  return '<hr>';
}

Any info on this (even advice on how to debug, add logs, etc) would be great! Thanks!

nozer commented

@dasmikko
I think best way to handle your custom attributes is to make the op a custom blot.
So, in your case, converting your op to this:

{
    "ops": [
        {
            "attributes": {
            },
            "insert": {"spoiler": "Testing spoiler tags"}
        },
        {
            "insert": ". ignore this\n"
        }
    ]
}

and then rendering your custom blot via this:

converter.renderCustomWith((op, ctxop) => {
   if (op.insert.type === 'spoiler') {
      return `<spoiler>${op.insert.value}</spoiler>`;
   }
})

would give your desired output.

nozer commented

@vahaknp
This op:

{
  "insert": "\n",
   "attributes": {
      "horizontalrule": true
    }
},

cannot be converted with .renderCustomWith because it is not a custom op.
For it to be a custom op, insert property's value must be an object with just one key and key name cannot be one of the built-in types (which are image, video, formula).

So, you can rewrite that op as a custom op like this:

{
  "insert": {"horizontalrule": true},
   "attributes": {
    }
},

then, you can render it with this:

converter.renderCustomWith((op, ctxop) => {
   if (op.insert.type === 'horizontalrule') {
      return `<hr>`;
   }
})

which will output a result like this: <p><hr></p>

and if you don't want the <p> tag around the hr, then you can do this:

converter.afterRender((group, html) => {
   if (html === '<p><hr></p>') {
      return '<hr>';
   }
   return html;
});

@nozer thanks for the help!

nozer commented

You are welcome. Sorry for the delay