Mercurial > code > home > repos > streamed-graph
view src/render/GraphView.ts @ 108:5e6840229a05
rewrite freeStatements rendering to put more planning in layout
author | drewp@bigasterisk.com |
---|---|
date | Fri, 18 Mar 2022 11:57:38 -0700 |
parents | 042bd3361339 |
children | cbcd82d21356 |
line wrap: on
line source
import Immutable from "immutable"; import { html, TemplateResult } from "lit"; import { DataFactory, Literal, NamedNode, Quad, Store, Term } from "n3"; import { NodeDisplay } from "./NodeDisplay"; import { SuffixLabels } from "../layout/suffixLabels"; import { AlignedTable, FreeStatements, Layout, PredRow, SubjRow } from "../layout/Layout"; import { TableDesc, ViewConfig } from "../layout/ViewConfig"; import { uniqueSortedTerms } from "../layout/rdf_value"; const { namedNode } = DataFactory; type UriSet = Immutable.Set<NamedNode>; const emptyUriSet = Immutable.Set<NamedNode>(); // 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 { nodeDisplay!: NodeDisplay; constructor( public dataSourceUrls: string[], public graph: Store, public viewConfig?: ViewConfig ) {} async makeTemplate(): Promise<TemplateResult> { const layout = new Layout(this.viewConfig); const lr = layout.plan(this.graph); const labels = new SuffixLabels(); this._addLabelsForAllTerms(this.graph, labels); this.nodeDisplay = new NodeDisplay(labels); let viewTitle = html` (no view)`; if (this.viewConfig?.url) { viewTitle = html` using view <a href="${this.viewConfig.url}">${this.viewConfig.label()}</a>`; } // const tables = this.view.toplevelTables(typesPresent); return html` <section> <h2> Current graph (<a href="${this.dataSourceUrls[0]}" >${this.dataSourceUrls[0]}</a >)${viewTitle} </h2> <div> <!-- todo: graphs and provenance. These statements are all in the <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.--> </div> ${lr.sections.map(this._renderSection.bind(this))} </section> `; } _addLabelsForAllTerms(graph: Store, labels: SuffixLabels) { 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 ); } _renderSection(section: AlignedTable | FreeStatements) { if ((section as any).columnHeaders) { return this._renderAlignedTable(section as AlignedTable); } else { return this._renderFreeStatements(section as FreeStatements); } } _renderAlignedTable(section: AlignedTable): TemplateResult { return html`aligned table section`; } _renderFreeStatements(section: FreeStatements): TemplateResult { const subjects: NamedNode[] = []; let subjPreds = Immutable.Map<NamedNode, UriSet>(); return html`<div class="spoGrid"> grid has rowcount ${section.subjRows.length} ${section.subjRows.map(this._subjPredObjsBlock.bind(this))} </div>`; } _subjPredObjsBlock( row: SubjRow ): TemplateResult { return html` <div class="subject"> ${this.nodeDisplay.render(row.subj)} <!-- todo: special section for uri/type-and-icon/label/comment --> <div> ${row.predRows.map(this._predObjsBlock.bind(this))} </div> </div> `; } _predObjsBlock(row: PredRow): TemplateResult { return html` <div class="predicate"> ${this.nodeDisplay.render(row.pred)} <div>${row.objs.map(this._objCell.bind(this))}</div> </div> `; } _objCell(obj: Term): TemplateResult { return html` <div class="object"> ${this.nodeDisplay.render(obj)} <!-- indicate what source or graph said this stmt --> </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> // `; // } }