Mercurial > code > home > repos > streamed-graph
view src/elements/graph-view/GraphView.ts @ 128:5a1a79f54779
big rewrite
author | drewp@bigasterisk.com |
---|---|
date | Fri, 05 May 2023 21:26:36 -0700 |
parents | src/render/GraphView.ts@c2923b20bf5c |
children | cf642d395be4 |
line wrap: on
line source
import Immutable from "immutable"; import { LitElement, PropertyValues, TemplateResult, html } from "lit"; import { customElement, property } from "lit/decorators.js"; import { NamedNode, Quad, Store, Term } from "n3"; import { MultiStore } from "../../MultiStore"; import { Patch } from "../../Patch"; import { AlignedTable, FreeStatements, Layout, PredRow, SubjRow, } from "../../layout/Layout"; import { ViewConfig } from "../../layout/ViewConfig"; import { uniqueSortedTerms } from "../../layout/rdf_value"; import { SuffixLabels } from "../../layout/suffixLabels"; import { graphViewStyle, pageStyle } from "../../style"; import { NodeDisplay } from "./NodeDisplay"; type UriSet = Immutable.Set<NamedNode>; @customElement("graph-view") export class GraphView extends LitElement { @property() graph: MultiStore | null = null; @property() viewEl: HTMLElement | null = null; viewConfig: ViewConfig | null = null; nodeDisplay: NodeDisplay | null = null; constructor() { super(); } static styles = [pageStyle, graphViewStyle]; update(changedProperties: PropertyValues) { if (changedProperties.has("graph") && this.graph) { // const viewUri = new NamedNode(this.viewEl?.getAttribute('uri')) const viewUri = new NamedNode(new URL("#view", document.baseURI).href); this.viewConfig = new ViewConfig(this.graph, viewUri); // "when viewconfig is updated..." setTimeout(()=>this.requestUpdate(), 1000) this.graph.graphChanged.subscribe(this.onChange?.bind(this)); } super.update(changedProperties); } onChange(p: Patch) { this.requestUpdate(); } render() { if (!this.graph) { return; } return this.makeTemplate(this.graph); } makeTemplate(graph: MultiStore): TemplateResult { if (!this.viewConfig) { throw new Error(); } const layout = new Layout(this.viewConfig); const lr = layout.plan(graph); const labels = new SuffixLabels(); this._addLabelsForAllTerms(graph, labels); labels.planDisplayForNode(this.viewConfig.viewRoot); // todo shoudltn be needed this.nodeDisplay = new NodeDisplay(labels); let viewTitle = html` (no view)`; viewTitle = html` using view <a href="${this.viewConfig.viewRoot.value}" >{this.nodeDisplay.render(this.viewConfig.viewRoot)}</a >`; return html` <section> <h2>View: ${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 { const nodeDisplay = this.nodeDisplay; if (!nodeDisplay) throw new Error(); const tableTypes: NamedNode[][] = []; const typeHeads: TemplateResult[] = []; const heads: TemplateResult[] = []; for (let ch of section.columnHeaders) { const colSpan = 1; //todo typeHeads.push( html`<th colspan="${colSpan}"> ${ch.rdfTypes.map((n) => nodeDisplay.render(n))} </th>` ); tableTypes.push(ch.rdfTypes); heads.push(html`<th>${nodeDisplay.render(ch.pred)}</th>`); } const cells = []; for (let rowIndex in section.rows) { const headerCol = nodeDisplay.render(section.rowHeaders[rowIndex]); const bodyCols = []; for (let cellObjs of section.rows[rowIndex]) { const display = cellObjs.map( (t) => html`<div>${nodeDisplay.render(t)}</div>` ); bodyCols.push(html`<td>${display}</td>`); } cells.push( html`<tr> <th>${headerCol}</th> ${bodyCols} </tr>` ); } const tableTypesUnique = uniqueSortedTerms(tableTypes.flat()); const typesDisplay = html`${tableTypesUnique.length == 1 ? "type" : "types"} ${tableTypesUnique.map((n) => nodeDisplay.render(n))}`; return html` <div>[icon] Resources of ${typesDisplay}</div> <div class="typeBlockScroll"> <table class="typeBlock"> <thead> <tr> <th></th> ${typeHeads} </tr> <tr> <th>Subject</th> ${heads} </tr> </thead> <tbody> ${cells} </tbody> </table> </div> `; } _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> `; } // 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> // `; // } } declare global { interface HTMLElementTagNameMap { "graph-view": GraphView; } }