JSFiddle - React, Tailwind, and code Playground

by Digvijay Naruka

HTML

<script src="//cdn.quilljs.com/1.3.6/quill.js"></script>
<link rel="stylesheet" href="//cdn.quilljs.com/1.3.6/quill.snow.css">
<div id="toolbar">
<button id='btn-separator' class='ql-button'>
 Hierarchy separator
</button>
</div>
 
<div id="editor-container">
</div>

SCSS

#editor-container {
  height: 375px;
}

.toolbar .ql-button {
  background: rgb(231, 237, 245) !important;
  border-color: rgb(158, 176, 199) !important;
  padding: 4px 6px !important;
  width: max-content !important;
  margin: 0 3px 0 3px !important;
}

div#editor-container .ql-editor {

  height: 100px;
  font-size: 16px !important;

  & * {
    font-size: 14px !important;
  }

  .ql-sep::before {
    background: lightgrey;
    padding: 2px 4px;
    display: inline-block;
    content: ',';
  }

  s {
    text-decoration: line-through !important;
  }

  em {
    filter: blur(0.05em);
  }

  em:hover {
    filter: blur(0);
  }

  .hidden-preview, .excluded-search    ,.hierarchy-separator {
            position: relative;
            opacity: 0.5;
            color: gray;
            font-style: italic;
        }
          .hidden-preview:hover, .excluded-search:hover    ,.hierarchy-separator:hover {
            opacity: 1;
            color: black;
        }
        .hidden-preview {
            border: 2px dashed red;
            background-color: rgba(255, 0, 0, 0.1);
        }
        .excluded-search {
            border: 2px dashed blue;
            background-color: rgba(0, 0, 255, 0.1);
        }
        .hidden-preview::before {
            content: 'Hidden';
            position: absolute;
             top: 100%;
            right: 0;
            background-color: red;
            color: white;
            padding: 2px 5px;
            font-size: 10px;
        }
        .excluded-search::before {
            content: 'Not Searchable';
            position: absolute;
            top: 100%;
            background-color: blue;
            color: white;
            padding: 2px 5px;
            font-size: 10px;
        }
        
        .hierarchy-separator {
             color: red ;
            font-weight: bold;
            position: relative;
            display: inline-block;
            padding-bottom: 10px;
        }
     ...

JavaScript

const Parchment = Quill.imports.parchment;
const Delta = Quill.imports.delta;

class Label extends Parchment.Embed {
  static create(value) {
    const node = super.create(value);
    node.innerText = value;
    node.contentEditable = 'false';
    //this._addRemovalButton(node);
    return node;
  }

  static value(node) {
    return node.childNodes[0].textContent;
  }

  static _addRemovalButton(node) {
    const button = document.createElement('button');
    button.innerText = 'x';
    button.onclick = () => node.remove();
    button.contentEditable = 'false';
    node.appendChild(button);

    // Extra span forces the cursor to the end of the label, otherwise it appears inside the removal button
    const span = document.createElement('span');
    span.innerText = ' ';
    node.appendChild(span);
  }
}
Label.blotName = 'sep';
Label.tagName = 'SPAN';
Label.className = 'hierarchy-separator';

Quill.register(Label);

var quill = new Quill('#editor-container', {
  modules: {
    toolbar: '#toolbar'
  },
  placeholder: 'Compose an epic...',
  theme: 'snow' // or 'bubble'
});

const customButton = document.querySelector('#btn-separator');
customButton.addEventListener('click', function() {
   let selection = quill.getSelection();
   
    var caretPosition = quill.getSelection(true);
    /* quill.insert(caretPosition, {sep:',' } );*/
    console.log(caretPosition)
   quill.updateContents(new Delta()
    .retain(caretPosition.index)      
    .insert({sep: ','})
     );
    quill.setSelection(caretPosition.index + 1);
});


quill.updateContents(new Delta().insert('foo').insert({
  sep: ','
}));