Mercurial > code > home > repos > streamed-graph
comparison src/render/GraphView.ts @ 107:042bd3361339
renames
author | drewp@bigasterisk.com |
---|---|
date | Sun, 13 Mar 2022 22:02:30 -0700 |
parents | src/render/graph_view.ts@2468f2227d22 |
children | 5e6840229a05 |
comparison
equal
deleted
inserted
replaced
106:2468f2227d22 | 107:042bd3361339 |
---|---|
1 import { html, TemplateResult } from "lit"; | |
2 import { DataFactory, Literal, NamedNode, Quad, Store, Term } from "n3"; | |
3 import { NodeDisplay } from "./NodeDisplay"; | |
4 import { SuffixLabels } from "../layout/suffixLabels"; | |
5 import { Layout } from "../layout/Layout"; | |
6 import { TableDesc, ViewConfig } from "../layout/ViewConfig"; | |
7 | |
8 const { namedNode } = DataFactory; | |
9 | |
10 // https://github.com/rdfjs/N3.js/issues/265 | |
11 if ((Literal.prototype as any).hashCode === undefined) { | |
12 (Literal.prototype as any).hashCode = () => 0; | |
13 } | |
14 if ((NamedNode.prototype as any).hashCode === undefined) { | |
15 (NamedNode.prototype as any).hashCode = () => 0; | |
16 } | |
17 export class GraphView { | |
18 url: string; | |
19 view: View; | |
20 graph: Store; | |
21 nodeDisplay: NodeDisplay; | |
22 constructor(url: string, viewUrl: string, graph: Store) { | |
23 this.url = url; | |
24 this.view = new View(viewUrl); | |
25 this.graph = graph; | |
26 | |
27 const labels = new SuffixLabels(); | |
28 this._addLabelsForAllTerms(this.graph, labels); | |
29 | |
30 this.view.ready.then(() => { | |
31 this._addLabelsForAllTerms(this.view.graph, labels); | |
32 }); | |
33 this.nodeDisplay = new NodeDisplay(labels); | |
34 } | |
35 | |
36 _addLabelsForAllTerms(graph: Store, labels: SuffixLabels) { | |
37 graph.forEach( | |
38 (q: Quad) => { | |
39 if (q.subject.termType === "NamedNode") { | |
40 labels.planDisplayForNode(q.subject); | |
41 } | |
42 if (q.predicate.termType === "NamedNode") { | |
43 labels.planDisplayForNode(q.predicate); | |
44 } | |
45 if (q.object.termType === "NamedNode") { | |
46 labels.planDisplayForNode(q.object); | |
47 } | |
48 if (q.object.termType === "Literal" && q.object.datatype) { | |
49 labels.planDisplayForNode(q.object.datatype); | |
50 } | |
51 }, | |
52 null, | |
53 null, | |
54 null, | |
55 null | |
56 ); | |
57 } | |
58 | |
59 _subjPredObjsBlock(subj: NamedNode) { | |
60 const columns = predsForSubj(this.graph, subj); | |
61 return html` | |
62 <div class="subject"> | |
63 ${this.nodeDisplay.render(subj)} | |
64 <!-- todo: special section for uri/type-and-icon/label/comment --> | |
65 <div> | |
66 ${columns.map((p) => { | |
67 return this._predObjsBlock(subj, p); | |
68 })} | |
69 </div> | |
70 </div> | |
71 `; | |
72 } | |
73 | |
74 _objCell(obj: Term) { | |
75 return html` | |
76 <div class="object"> | |
77 ${this.nodeDisplay.render(obj)} | |
78 <!-- indicate what source or graph said this stmt --> | |
79 </div> | |
80 `; | |
81 } | |
82 | |
83 _predObjsBlock(subj: NamedNode, pred: NamedNode) { | |
84 const objsSet = new Set<Term>(); | |
85 this.graph.forEach( | |
86 (q: Quad) => { | |
87 objsSet.add(q.object); | |
88 }, | |
89 subj, | |
90 pred, | |
91 null, | |
92 null | |
93 ); | |
94 const objs = Array.from(objsSet.values()); | |
95 objs.sort(); | |
96 return html` | |
97 <div class="predicate"> | |
98 ${this.nodeDisplay.render(pred)} | |
99 <div>${objs.map(this._objCell.bind(this))}</div> | |
100 </div> | |
101 `; | |
102 } | |
103 | |
104 _drawObj(obj: Term): TemplateResult { | |
105 return html` <div>${this.nodeDisplay.render(obj)}</div> `; | |
106 } | |
107 | |
108 _drawColumnHead(pred: NamedNode): TemplateResult { | |
109 return html` <th>${this.nodeDisplay.render(pred)}</th> `; | |
110 } | |
111 | |
112 _thead(layout: MultiSubjsTypeBlockLayout): TemplateResult { | |
113 return html` | |
114 <thead> | |
115 <tr> | |
116 <th></th> | |
117 ${layout.preds.map(this._drawColumnHead.bind(this))} | |
118 </tr> | |
119 </thead> | |
120 `; | |
121 } | |
122 | |
123 _msbCell(layout: MultiSubjsTypeBlockLayout, subj: NamedNode) { | |
124 return (pred: NamedNode): TemplateResult => { | |
125 const objs = layout.graphCells.get(layout.makeCellKey(subj, pred)); | |
126 if (!objs || !objs.size) { | |
127 return html` <td></td> `; | |
128 } | |
129 const objsList = Array.from(objs); | |
130 objsList.sort(); | |
131 return html` <td>${objsList.map(this._drawObj.bind(this))}</td> `; | |
132 }; | |
133 } | |
134 | |
135 _instanceRow(layout: MultiSubjsTypeBlockLayout) { | |
136 return (subj: NamedNode): TemplateResult => { | |
137 return html` | |
138 <tr> | |
139 <td>${this.nodeDisplay.render(subj)}</td> | |
140 ${layout.preds.map(this._msbCell(layout, subj))} | |
141 </tr> | |
142 `; | |
143 }; | |
144 } | |
145 | |
146 _multiSubjsTypeBlock(byType: TypeToSubjs, table: TableDesc) { | |
147 const layout = new MultiSubjsTypeBlockLayout(this.graph, byType, table); | |
148 | |
149 let typeNames = [html`${this.nodeDisplay.render(table.primary)}`]; | |
150 if (table.joins) { | |
151 typeNames.push(html` joined with [`); | |
152 for (let j of table.joins) { | |
153 typeNames.push(html`${this.nodeDisplay.render(j)}`); | |
154 } | |
155 typeNames.push(html`]`); | |
156 } | |
157 | |
158 return html` | |
159 <div>[icon] Resources of type ${typeNames}</div> | |
160 <div class="typeBlockScroll"> | |
161 <table class="typeBlock"> | |
162 ${this._thead(layout)} | |
163 ${layout.subjs.map(this._instanceRow(layout))} | |
164 </table> | |
165 </div> | |
166 `; | |
167 } | |
168 | |
169 async makeTemplate(): Promise<TemplateResult> { | |
170 await this.view.ready; | |
171 const { byType, typesPresent, untypedSubjs } = groupByRdfType(this.graph); | |
172 let viewTitle = html` (no view)`; | |
173 if (this.view.url) { | |
174 viewTitle = html` using view | |
175 <a href="${this.view.url}">${this.view.label()}</a>`; | |
176 } | |
177 const tables = this.view.toplevelTables(typesPresent); | |
178 return html` | |
179 <section> | |
180 <h2> | |
181 Current graph (<a href="${this.url}">${this.url}</a>)${viewTitle} | |
182 </h2> | |
183 <div> | |
184 <!-- todo: graphs and provenance. | |
185 These statements are all in the | |
186 <span data-bind="html: $root.createCurie(graphUri())">...</span> graph.--> | |
187 </div> | |
188 ${tables.map((t: TableDesc) => this._multiSubjsTypeBlock(byType, t))} | |
189 <div class="spoGrid"> | |
190 ${untypedSubjs.map(this._subjPredObjsBlock.bind(this))} | |
191 </div> | |
192 </section> | |
193 `; | |
194 } | |
195 } |