[anvil-extras] Trouble customizing Quill toolbar buttons

I’d like to use the Quill editor with some custom toolbar buttons. I can customize the toolbar itself by rearranging the built-in toolbar buttons using code like this:

    self.quill_1.toolbar = [
      ['bold', 'italic', 'underline', 'strike'],
      ['blockquote', 'code-block'],
      ['clean']
    ]

And I can create a handler in Python for any of those built-in buttons:

    toolbar = self.quill_1.get_module('toolbar')
    toolbar.addHandler('bold', self.bold)
    
  def bold(self, args):
    print("called")
    print(args)

Where I’m having issues is twofold.

Problem #1

I haven’t been able to work out how to get a new toolbar image into the app. The suggestions I’m finding online involved code like this:

icons = self.quill_1.import('ui/icons');

But import isn’t a legal Python method name, so I can’t use that part of Quill’s API, if it’s even exposed.

I also tried just filling the button with a red square with this CSS, but it did not work:

.ql-toolbar .ql-foo {
    fill: red;
}

Problem #2

I haven’t been able to hook up a handler for the custom button. Doing the same thing from above for a custom button tag:

    self.quill_1.toolbar = [
      ['bold', 'italic', 'underline', 'strike', 'foo'],
      ['blockquote', 'code-block'],
      ['clean']
    ]
    
    toolbar = self.quill_1.get_module('toolbar')
    toolbar.addHandler('foo', self.foo)
    
  def foo(self, args):
    print("called")

The space for the toolbar button is reserved in the toolbar with a div, the same as all the others, but clicking it does not trigger the handler function.

I don’t know if this has to do with the lack of an image or something else.

Here’s a clone link. The QuillTesting form has the code I have so far.

It’s all doable.
What do you want the custom button to be able to do?


How do you get the javascript Quill object and use the import?

import anvil_extras.Quill # this will add Quill to window
from anvil.js.window import Quill
icons = Quill["import"]("ui/icons")
# js proxy objects can use subscript notation 
# which gets around using .import
# (we could also have used getattr(Quill, "import")
icons['foo'] = '<i class="fa fa-eye"></i>'
# taken from a quill github issue https://github.com/quilljs/quill/issues/1099

How to use a custom button in Quill?

It’s probably not the right approach since it’s not documented in Quill.
But, that placeholder element for the button foo adds a class ql-foo to the button
We can access that button using some anvil.js

    quill_dom = anvil.js.get_dom_node(self.quill_1)
    foo_btn = quill_dom.querySelector('.ql-foo')
    foo_btn.addEventListener('click', self.foo)

We can’t use the getModule('toolbar') approach since quill doesn’t know anything about a foo button control. Whereas it knows about a bold button control.

Note:
The suggested approach to custom buttons in the quill docs is to create your own toolbar container

This would work too but it’s (even) more of a faff.

I’m looking to have a variety of buttons. Most will prompt the user for additional info and then either inject specific text at the cursor, or surround selected text with specific tags.

I’d previously been storing all the markup as BBCode. Some of it will still need to be stored that way, but using Quill opens up the possibility of both providing easy shortcuts for entering BBCode and storing some of the simpler markup as HTML.

Thanks for the pointers on how to set the button icon and handle the listeners for the custom buttons. I’ll work with those and see what I can do.

Edit: that ended up being easier than I thought it would, once the syntax was right. For anyone finding this post later, here’s a sample for adding a new button and getting it to wrap the selected text in whatever you want.

from ._anvil_designer import QuillTestingTemplate
from anvil import *

import anvil_extras.Quill 
from anvil.js.window import Quill

class QuillTesting(QuillTestingTemplate):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
    
    # Icons must be setup before the toolbar is
    # or the toolbar won't pick up the icons for 
    # the custom buttons
    icons = Quill["import"]("ui/icons")
    icons['foo'] = '<i class="fa fa-eye"></i>'
    
    # Then setup the toolbar
    self.quill_1.toolbar = [
      ['bold', 'italic', 'underline', 'strike', 'foo'],
      ['blockquote', 'code-block'],
      ['clean']
    ]
    
    # That sets up the CSS class for the custom
    # buttons, so we can hook in a click handler
    quill_dom = js.get_dom_node(self.quill_1)
    foo_btn = quill_dom.querySelector('.ql-foo')
    foo_btn.addEventListener('click', self.foo)    
    
  def foo(self, args):
    sel = self.quill_1.get_selection()
    
    if sel:
      text = self.quill_1.get_text(sel)
      self.quill_1.delete_text(sel)
      self.quill_1.insert_text(sel.index, '[foo]'+text+'[/foo]')
2 Likes