Mercurial > code > home > repos > streamed-graph
diff 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 diff
--- a/src/render/GraphView.ts Sun Mar 13 22:02:30 2022 -0700 +++ b/src/render/GraphView.ts Fri Mar 18 11:57:38 2022 -0700 @@ -1,12 +1,16 @@ +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 { Layout } from "../layout/Layout"; +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; @@ -15,22 +19,42 @@ (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; + 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.view.ready.then(() => { - this._addLabelsForAllTerms(this.view.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) { @@ -55,48 +79,54 @@ null ); } + _renderSection(section: AlignedTable | FreeStatements) { + if ((section as any).columnHeaders) { + return this._renderAlignedTable(section as AlignedTable); + } else { + return this._renderFreeStatements(section as FreeStatements); + } + } - _subjPredObjsBlock(subj: NamedNode) { - const columns = predsForSubj(this.graph, subj); + _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(subj)} + ${this.nodeDisplay.render(row.subj)} <!-- todo: special section for uri/type-and-icon/label/comment --> <div> - ${columns.map((p) => { - return this._predObjsBlock(subj, p); - })} + ${row.predRows.map(this._predObjsBlock.bind(this))} </div> </div> `; } - _objCell(obj: Term) { + _predObjsBlock(row: PredRow): TemplateResult { return html` - <div class="object"> - ${this.nodeDisplay.render(obj)} - <!-- indicate what source or graph said this stmt --> + <div class="predicate"> + ${this.nodeDisplay.render(row.pred)} + <div>${row.objs.map(this._objCell.bind(this))}</div> </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(); + _objCell(obj: Term): TemplateResult { return html` - <div class="predicate"> - ${this.nodeDisplay.render(pred)} - <div>${objs.map(this._objCell.bind(this))}</div> + <div class="object"> + ${this.nodeDisplay.render(obj)} + <!-- indicate what source or graph said this stmt --> </div> `; } @@ -109,87 +139,59 @@ 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> - `; - } + // _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); + // _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> `; + // }; + // } - 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`]`); - } + // _instanceRow(layout: MultiSubjsTypeBlockLayout) { + // return (subj: NamedNode): TemplateResult => { + // return html` + // <tr> + // <td>${this.nodeDisplay.render(subj)}</td> + // ${layout.preds.map(this._msbCell(layout, subj))} + // </tr> + // `; + // }; + // } - 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> - `; - } + // _multiSubjsTypeBlock(byType: TypeToSubjs, table: TableDesc) { + // const layout = new MultiSubjsTypeBlockLayout(this.graph, byType, table); - 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> - `; - } + // 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> + // `; + // } }