view src/index.ts @ 77:0ffe5591b048

sort imports
author drewp@bigasterisk.com
date Tue, 11 Feb 2020 22:54:47 -0800
parents 1bb65cb4c685
children 0c188ed3bcd8
line wrap: on
line source

import { render } from 'lit-html';
import { N3Store, Store } from 'n3';

import { computed, customElement, property } from '@polymer/decorators';
import { html, PolymerElement } from '@polymer/polymer';

import { GraphView } from './graph_view';
import { StreamedGraphClient } from './streamed_graph_client';
import style from './style.styl';

export * from "./graph_queries";

export interface VersionedGraph {
  version: number;
  store: N3Store;
}

function templateWithStyle(style: string, tmpl: HTMLTemplateElement) {
  const styleEl = document.createElement("style");
  styleEl.textContent = style;
  tmpl.content.insertBefore(styleEl, tmpl.content.firstChild);
  return tmpl;
}

@customElement("streamed-graph")
export class StreamedGraph extends PolymerElement {
  @property({ type: String })
  url: string = "";

  @property({ type: Object, notify: true })
  graph!: VersionedGraph;

  @property({ type: Boolean })
  expanded: boolean = false;

  @computed("expanded")
  get expandAction() {
    return this.expanded ? "-" : "+";
  }

  @property({ type: String })
  status: string = "";

  sg!: StreamedGraphClient;
  graphViewEl!: Element;
  graphViewDirty = true;

  static get template() {
    return templateWithStyle(
      style,
      html`
        <div id="ui">
          <span class="expander"
            ><button on-click="toggleExpand">{{expandAction}}</button></span
          >
          StreamedGraph <a href="{{url}}">[source]</a>: {{status}}
        </div>
        <div id="graphView"></div>
      `
    );
  }

  ready() {
    super.ready();
    const emptyStore = new Store();
    this.graph = { version: -1, store: emptyStore };
    this.graphViewEl = (this.shadowRoot as ShadowRoot).getElementById(
      "graphView"
    ) as Element;

    this._onUrl(this.url); // todo: watch for changes and rebuild
    if (this.expanded) {
      this.redrawGraph();
    }
  }

  toggleExpand() {
    this.expanded = !this.expanded;
    if (this.expanded) {
      this.redrawGraph();
    } else {
      this.graphViewDirty = false;
      this._graphAreaClose();
    }
  }

  redrawGraph() {
    this.graphViewDirty = true;
    requestAnimationFrame(this._redrawLater.bind(this));
  }

  _onUrl(url: string) {
    if (this.sg) {
      this.sg.close();
    }
    this.sg = new StreamedGraphClient(
      url,
      this.onGraphChanged.bind(this),
      this.set.bind(this, "status"),
      [], //window.NS,
      []
    );
  }

  onGraphChanged() {
    this.graph = {
      version: this.graph.version + 1,
      store: this.sg.store
    };
    if (this.expanded) {
      this.redrawGraph();
    }
    this.dispatchEvent(
      new CustomEvent("graph-changed", { detail: { graph: this.graph } })
    );
  }

  _redrawLater() {
    if (!this.graphViewDirty) return;

    if ((this.graph as VersionedGraph).store && this.graph.store) {
      this._graphAreaShowGraph(new GraphView(this.url, this.graph.store));
      this.graphViewDirty = false;
    } else {
      this._graphAreaShowPending();
    }
  }

  _graphAreaClose() {
    render(null, this.graphViewEl);
  }

  _graphAreaShowPending() {
    render(
      html`
        <span>waiting for data...</span>
      `,
      this.graphViewEl
    );
  }

  _graphAreaShowGraph(graphView: GraphView) {
    render(graphView.makeTemplate(), this.graphViewEl);
  }
}