diff src/render/graph_view.ts @ 106:2468f2227d22

make src/layout/ and src/render/ separation
author drewp@bigasterisk.com
date Sun, 13 Mar 2022 22:00:30 -0700
parents src/graph_view.ts@26c55d5d5202
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/render/graph_view.ts	Sun Mar 13 22:00:30 2022 -0700
@@ -0,0 +1,196 @@
+import { html, TemplateResult } from "lit";
+import { DataFactory, Literal, NamedNode, Quad, Store, Term } from "n3";
+import { NodeDisplay } from "./NodeDisplay";
+import { SuffixLabels } from "../layout/suffixLabels";
+import { Layout } from "../layout/Layout";
+import { TableDesc, ViewConfig } from "../layout/ViewConfig";
+
+const { namedNode } = DataFactory;
+
+// https://github.com/rdfjs/N3.js/issues/265
+if ((Literal.prototype as any).hashCode === undefined) {
+  (Literal.prototype as any).hashCode = () => 0;
+}
+if ((NamedNode.prototype as any).hashCode === undefined) {
+  (NamedNode.prototype as any).hashCode = () => 0;
+}
+export class GraphView {
+  url: string;
+  view: View;
+  graph: Store;
+  nodeDisplay: NodeDisplay;
+  constructor(url: string, viewUrl: string, graph: Store) {
+    this.url = url;
+    this.view = new View(viewUrl);
+    this.graph = graph;
+
+    const labels = new SuffixLabels();
+    this._addLabelsForAllTerms(this.graph, labels);
+    
+    if (this.view.graph) {
+      this._addLabelsForAllTerms(this.view.graph, labels);
+    }
+    this.nodeDisplay = new NodeDisplay(labels);
+  }
+
+  _addLabelsForAllTerms(graph: Store, labels: SuffixLabels) {
+    console.log("_addLabelsForAllTerms");
+
+    graph.forEach(
+      (q: Quad) => {
+        if (q.subject.termType === "NamedNode") {
+          labels.planDisplayForNode(q.subject);
+        }
+        if (q.predicate.termType === "NamedNode") {
+          labels.planDisplayForNode(q.predicate);
+        }
+        if (q.object.termType === "NamedNode") {
+          labels.planDisplayForNode(q.object);
+        }
+        if (q.object.termType === "Literal" && q.object.datatype) {
+          labels.planDisplayForNode(q.object.datatype);
+        }
+      },
+      null,
+      null,
+      null,
+      null
+    );
+  }
+
+  _subjPredObjsBlock(subj: NamedNode) {
+    const columns = predsForSubj(this.graph, subj);
+    return html`
+      <div class="subject">
+        ${this.nodeDisplay.render(subj)}
+        <!-- todo: special section for uri/type-and-icon/label/comment -->
+        <div>
+          ${columns.map((p) => {
+            return this._predObjsBlock(subj, p);
+          })}
+        </div>
+      </div>
+    `;
+  }
+
+  _objCell(obj: Term) {
+    return html`
+      <div class="object">
+        ${this.nodeDisplay.render(obj)}
+        <!-- indicate what source or graph said this stmt -->
+      </div>
+    `;
+  }
+
+  _predObjsBlock(subj: NamedNode, pred: NamedNode) {
+    const objsSet = new Set<Term>();
+    this.graph.forEach(
+      (q: Quad) => {
+        objsSet.add(q.object);
+      },
+      subj,
+      pred,
+      null,
+      null
+    );
+    const objs = Array.from(objsSet.values());
+    objs.sort();
+    return html`
+      <div class="predicate">
+        ${this.nodeDisplay.render(pred)}
+        <div>${objs.map(this._objCell.bind(this))}</div>
+      </div>
+    `;
+  }
+
+  _drawObj(obj: Term): TemplateResult {
+    return html` <div>${this.nodeDisplay.render(obj)}</div> `;
+  }
+
+  _drawColumnHead(pred: NamedNode): TemplateResult {
+    return html` <th>${this.nodeDisplay.render(pred)}</th> `;
+  }
+
+  _thead(layout: MultiSubjsTypeBlockLayout): TemplateResult {
+    return html`
+      <thead>
+        <tr>
+          <th></th>
+          ${layout.preds.map(this._drawColumnHead.bind(this))}
+        </tr>
+      </thead>
+    `;
+  }
+
+  _msbCell(layout: MultiSubjsTypeBlockLayout, subj: NamedNode) {
+    return (pred: NamedNode): TemplateResult => {
+      const objs = layout.graphCells.get(layout.makeCellKey(subj, pred));
+      if (!objs || !objs.size) {
+        return html` <td></td> `;
+      }
+      const objsList = Array.from(objs);
+      objsList.sort();
+      return html` <td>${objsList.map(this._drawObj.bind(this))}</td> `;
+    };
+  }
+
+  _instanceRow(layout: MultiSubjsTypeBlockLayout) {
+    return (subj: NamedNode): TemplateResult => {
+      return html`
+        <tr>
+          <td>${this.nodeDisplay.render(subj)}</td>
+          ${layout.preds.map(this._msbCell(layout, subj))}
+        </tr>
+      `;
+    };
+  }
+
+  _multiSubjsTypeBlock(byType: TypeToSubjs, table: TableDesc) {
+    const layout = new MultiSubjsTypeBlockLayout(this.graph, byType, table);
+
+    let typeNames = [html`${this.nodeDisplay.render(table.primary)}`];
+    if (table.joins) {
+      typeNames.push(html` joined with [`);
+      for (let j of table.joins) {
+        typeNames.push(html`${this.nodeDisplay.render(j)}`);
+      }
+      typeNames.push(html`]`);
+    }
+
+    return html`
+      <div>[icon] Resources of type ${typeNames}</div>
+      <div class="typeBlockScroll">
+        <table class="typeBlock">
+          ${this._thead(layout)} ${layout.subjs.map(this._instanceRow(layout))}
+        </table>
+      </div>
+    `;
+  }
+
+  async makeTemplate(): Promise<TemplateResult> {
+    await this.view.ready;
+    const { byType, typesPresent, untypedSubjs } = groupByRdfType(this.graph);
+    let viewTitle = html` (no view)`;
+    if (this.view.url) {
+      viewTitle = html` using view
+        <a href="${this.view.url}">${this.view.label()}</a>`;
+    }
+    const tables = this.view.toplevelTables(typesPresent);
+    return html`
+      <section>
+        <h2>
+          Current graph (<a href="${this.url}">${this.url}</a>)${viewTitle}
+        </h2>
+        <div>
+          <!-- todo: graphs and provenance.
+            These statements are all in the
+            <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.-->
+        </div>
+        ${tables.map((t: TableDesc) => this._multiSubjsTypeBlock(byType, t))}
+        <div class="spoGrid">
+          ${untypedSubjs.map(this._subjPredObjsBlock.bind(this))}
+        </div>
+      </section>
+    `;
+  }
+}