gwtgwt-jsinterop

Javascript module function in GWT with JsInterop


Hoping this is way easier than I'm making it - I'm a Java coder, some inner Javascript aspects are a tad unfamiliar to me.

Trying to embed the great CodeJar library inside a GWT panel. There's a pretty nice/simple example for CodeJar:

<script type="module">
  import {CodeJar} from './codejar.js'
  import {withLineNumbers} from './linenumbers.js';

  const editor = document.querySelector('.editor')

  const highlight = editor => {
    // highlight.js does not trim old tags,
    // let's do it by this hack.
    editor.textContent = editor.textContent
    hljs.highlightBlock(editor)
  }

  const jar = CodeJar(editor, withLineNumbers(highlight), {
    indentOn: /[(\[{]$/
  })

  jar.updateCode(localStorage.getItem('code'))
  jar.onUpdate(code => {
    localStorage.setItem('code', code)
  })
</script>

The module function itself looks like this:

export function CodeJar(editor, highlight, opt = {}) { ... }

'editor' is a Div reference, and 'highlight' is a callback library function for handling code highlighting.

What I'm battling with is the JsInterop markup and code to make Javascript modules work with GWT. The above has a few aspects which I'm battling with


Solution

  • To load the script and have it available for GWT consumption, you have (at least) 3 possibilities:

    Next, you need to create JsInterop bindings so you can call it from GWT; something like that (assuming you made CodeJar available globally as window.CodeJar, and using elemental2-dom for HTMLElement, but com.google.gwt.dom.client.Element would work just as well):

    @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "?")
    interface CodeJar {
      @JsMethod(namespace = JsPackage.GLOBAL, name = "CodeJar")
      static native CodeJar newInstance(HTMLElement element, HighlightFn highlight);
      @JsMethod(namespace = JsPackage.GLOBAL, name = "CodeJar")
      static native CodeJar newInstance(HTMLElement element, HighlightFn highlight, Options opts);
    
      void updateOptions(Options options);
      void updateCode(String code);
      void onUpdate(UpdateFn cb);
      void destroy();
    }
    
    @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
    class Options {
      public String tab;
      public JsRegExp indentOn;
      public boolean spellcheck;
      public boolean addClosing;
    }
    
    @JsFunction
    @FunctionalInterface
    interface HighlightFn {
      void highlight(HTMLElement e);
    }
    
    @JsFunction
    @FunctionalInterface
    interface UpdateFn {
      void onUpdate(String code);
    }
    

    With the above code, you should be able to create an editor using something like:

    CodeJar jar = CodeJar.newInstance(editor, MyHighlighter::highlight);
    

    If you use a dynamic import(), replace the static methods with instance ones in a @JsType interface representing the module received from the promise.