Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Atomic Text Blot in Quill

Tags:

quill

Let's say I want to have an AtomicText blot that is similar to the default Link blot but is immutable and can only be removed as a whole. More specifically:

  • The cursor can be between the characters of AtomicText.
  • It is possible to select parts of AtomicText.
  • Deleting at least one character of AtomicText leads to the deletion of the whole AtomicText.
  • Adding characters to AtomicText is not possible once it has been created. Neither via keyboard events nor via copy and paste.

My idea was to make AtomicText extend from the Embed blot. In that case, the whole AtomicText blot is deleted when the cursor is right to its last character and backspace is pressed. But other operations do not work as expected. I assume I need to override some of the Blot methods to achieve the correct behavior but I am a bit at loss here.

Another idea is to listen to text-change events, determine if the cursor is inside an AtomicText blot and act accordingly. E.g., when pressing backspace, find the start and end position of the current AtomicText blot and remove all characters between these indexes. This seems to be a fragile approach.

Any pointers would be appreciated.

Similar questions/requests are the following:

  • How to make non selectable embed custom format in quilljs
  • How to add a non editable tag to content in Quill editor
  • Quill Editor: Restricted Editing based on tags/classes
like image 432
Eugen Avatar asked Nov 28 '16 08:11

Eugen


2 Answers

I my experience setting contenteditable false is problematic. If you position the AtomicText blot at the end of the document you will not be able to append to it. Additionally, moving the cursor over the AtomicText positioned at the end will blur the editor.

I experienced a similar issue with a footnote blot. It's a sup tag that contains [\d+] and we don't want editors to modify the footnote but we do want them to be able to seamlessly move the cursor through them.

I have experimented with two other options in addition to the text-change listener mentioned by the OP. The first option is to attempt to position the cursor (via setSelection) in front or behind using selection-change event listeners. I found this to be problematic because it's possible to sneak characters in before the selection change event fires. I'm also not a fan of this approach because it causes the cursor to jump across the footnote. YMMV

Another option is to intercept keyboard input. You can add a keyboard listener (I used KeyboardJS) in a module that's initialized with quill. If the current selection is "inside" of your atomic text you can stop the input from proceeding to quill using e.preventDefault(). With this approach you'll also have to provide custom keyboard handlers in your quill configuration to override tab, enter, and possibly delete. Tabs are more of a challenge because there's a default tab handler provided by quill that takes precedence unless you override it. Within the custom handler you'll have to detect if the context is within your AtomicText. The context object will contain contain a format map that contains atomictext (your blot name) if it's in the context of AtomicText.

Note that if the user positions the cursor adjacent to your AtomicText, then from quill's perspective that's in the context of AtomicText. Our solution to that problem for input is to insert a zero width non breaking space, then allow the keyboard input to proceed. That "breaks" the cursor out of the AtomicText blot allowing insertion to proceed as expected. We then strip those characters out of the resulting HTML before saving the text.

Hope this helps.

like image 55
Damon Snyder Avatar answered Oct 23 '22 04:10

Damon Snyder


In the blot create(value) function, add this:

node.setAttribute('contenteditable', false);
like image 44
Andy Avatar answered Oct 23 '22 03:10

Andy