Mercurial > code > home > repos > streamed-graph
view 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 source
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> `; } }