munHunger
munHunger

Reputation: 3001

Adding syntax highlight to an input in svelte

I am trying to add an extremely simple "syntax highlight" to a text input in svelte.

Basically what I want is to highlight everything in the input that matches the regex /{.*?}/ in a different color, but I can't figure out how.

my latest experiment looks like this

<script>
  export let value;
  let editor;

  function update() {
    value = editor.textContent
  }
</script>

<div contenteditable="true"
bind:this={editor} on:input={update}>
  {#if value}
    {#each value.split(" ") as part, i}
      <span class={part.match(/{.*?}/gm) ? "text-nord-12" : "text-nord-4"}
        >{#if i !== 0}&nbsp;{/if}{part}</span
      >
    {/each}
  {/if}
</div>

and while it isn't perfect it sort of works, except that the cursor gets reset from time to time when editing, which I assume is because the editor content is getting replaced when updating.

So basically is there any way to bind the text value while still being able to dynamically add/remove html tags?

I can note as well that binding textContent does seem to work either as it removes all the highlighting when mounted... or I might just be doing something wrong with the binding

Upvotes: 1

Views: 1716

Answers (1)

voscausa
voscausa

Reputation: 11706

Will this solve your problem using a use:action, on:blur and a replacer?

With blur you have to leave (lose focus) the div to mark the div text, but the cursor will nog jump on every keystroke.

Code and repl: Highlight matched text in a contenteditable div with svelte

<script>
  let match = "svelte|input";  // pseudo rex
  let content = "Adding syntax highlight to an input in svelte using a svelte action";
  let editor;

  function updateContent() {
    if (editor) content = editor.textContent;
  }

  function marker(txt, rex) {
    function markTerm(term) {
      let cls = "red";
      return ` <mark class="no-clicks ${cls}">${term}</mark> `;
    }
    return txt.replace(rex, (term) => markTerm(term));
  }

  export function highlight(node, [val, text]) {
    function action() {
      console.log(text);
      if (text) node.innerHTML = marker(text, new RegExp(val, "g"));
    }
    action();
    return {
      update(obj) {
        [val, text] = obj;
        action();
      }
    };
  }
</script>

<div>
  <div contenteditable="true" use:highlight={[match, content]} 
    bind:this={editor} on:blur={updateContent} />
</div>

<style>
  :global(.red) {
    color: red;
  }
</style>

Upvotes: 2

Related Questions