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