How to use quills setSelection?

Viewed 544

I am writing a custom module for quilljs. It is a simple text macro replacement tool.

ie type ".hi", it will replace it with "hello".

Originally I was calling quill.setSelection(...) within the text-change event. It wasn't working Codepen Original. Eventually I found this in the docs

  • Changes to text may cause changes to the selection (ex. typing advances the cursor), however during the text-change handler, the selection is not yet updated, and native browser behavior may place it in an inconsistent state. Use selection-change or editor-change for reliable selection updates." Quill setSelection API docs

So I reworked the code Codepen Revised. It works, but it sure is ugly and cursor updating after the text insertion looks weird. I cannot believe there isn't a better/idiomatic way to do this.

Quill.register("modules/quicktext", function (quill, options) {
  let cache = "";
  let finalcaret = null;
  quill.on("editor-change", (eventName, ...args) => {
    if (eventName === "text-change" && args[2] === "user") {
      let [delta, oldDelta, source] = args;
      console.log(source, delta);
      let lastkey = delta.ops[delta.ops.length - 1]?.insert || "";
      if (delta.ops[delta.ops.length - 1]?.delete) {
        // handle delete key
        cache = cache.slice(0, -1);
        return;
      }
      if (lastkey && lastkey !== " ") {
        cache += lastkey;
        console.log("cache", cache, "lastkey", lastkey);
      } else if (cache) {
        // avoid initial call
        console.log("cache", cache, "lastkey", lastkey);
        reps.forEach((rep) => {
          console.log("rep check", cache, rep[cache]);
          if (rep[cache]) {
            console.log("triggered");
            let caret = quill.getSelection().index;
            let start = caret - cache.length - 1;
            quill.deleteText(start, cache.length, "api");
            quill.insertText(start, rep[cache], "api");
            //quill.update("api");
            finalcaret = caret + rep[cache].length - cache.length;
            console.log(
              `caret at ${caret}, moving forward ${
                rep[cache].length - cache.length
              } spaces, to position ${
                caret + rep[cache].length - cache.length
              }.`
            );
            console.log("done trigger");
          }
        });
        cache = "";
      }
    } else if (eventName === "selection-change") {
      if (finalcaret) {
        quill.setSelection(finalcaret);
        finalcaret = "";
      }
    }
  });
});

let reps = [
  { ".hi": "hello" },
  { ".bye": "goodbye" },
  { ".brb": "be right back" }
];

// We can now initialize Quill with something like this:
var quill = new Quill("#editor", {
  modules: {
    quicktext: {
      reps: reps
    }
  }
});

1 Answers

I was able to figure out a solution based upon this issue. Not sure why this is necessary, but works but it does.

// quill.setSelection(finalcaret, 0) // this fails
setTimeout(() => quill.setSelection(finalcaret, 0), 1); // this works

Codepen FINAL

Related