@matdata/yasr 5.5.0 → 5.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/ts/src/defaults.js +2 -2
- package/build/ts/src/defaults.js.map +1 -1
- package/build/ts/src/index.js +0 -2
- package/build/ts/src/index.js.map +1 -1
- package/build/ts/src/parsers/index.js +5 -1
- package/build/ts/src/parsers/index.js.map +1 -1
- package/build/ts/src/plugins/error/index.d.ts +1 -0
- package/build/ts/src/plugins/error/index.js +125 -1
- package/build/ts/src/plugins/error/index.js.map +1 -1
- package/build/yasr.min.css +1 -1
- package/build/yasr.min.css.map +3 -3
- package/build/yasr.min.js +45 -68
- package/build/yasr.min.js.map +4 -4
- package/package.json +1 -1
- package/src/defaults.ts +2 -2
- package/src/index.ts +0 -2
- package/src/parsers/index.ts +7 -1
- package/src/plugins/error/index.scss +29 -0
- package/src/plugins/error/index.ts +170 -1
- package/build/ts/src/plugins/table/__tests__/table-test.d.ts +0 -1
- package/build/ts/src/plugins/table/__tests__/table-test.js +0 -190
- package/build/ts/src/plugins/table/__tests__/table-test.js.map +0 -1
- package/build/ts/src/plugins/table/index.d.ts +0 -60
- package/build/ts/src/plugins/table/index.js +0 -453
- package/build/ts/src/plugins/table/index.js.map +0 -1
- package/src/plugins/table/__tests__/table-test.ts +0 -246
- package/src/plugins/table/column-resizer.d.ts +0 -29
- package/src/plugins/table/index.scss +0 -167
- package/src/plugins/table/index.ts +0 -557
|
@@ -1,557 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Make sure not to include any deps from our main index file. That way, we can easily publish the plugin as standalone build
|
|
3
|
-
*/
|
|
4
|
-
import "./index.scss";
|
|
5
|
-
import "datatables.net-dt/css/dataTables.dataTables.min.css";
|
|
6
|
-
import "datatables.net";
|
|
7
|
-
//@ts-ignore (jquery _does_ expose a default. In es6, it's the one we should use)
|
|
8
|
-
import $ from "jquery";
|
|
9
|
-
import Parser from "../../parsers";
|
|
10
|
-
import { escape } from "lodash-es";
|
|
11
|
-
import { Plugin, DownloadInfo } from "../";
|
|
12
|
-
import Yasr from "../../";
|
|
13
|
-
import { drawSvgStringAsElement, drawFontAwesomeIconAsSvg, addClass, removeClass } from "@matdata/yasgui-utils";
|
|
14
|
-
import * as faTableIcon from "@fortawesome/free-solid-svg-icons/faTable";
|
|
15
|
-
import { DeepReadonly } from "ts-essentials";
|
|
16
|
-
import { cloneDeep } from "lodash-es";
|
|
17
|
-
import sanitize from "../../helpers/sanitize";
|
|
18
|
-
import type { Api, ConfigColumns, CellMetaSettings, Config } from "datatables.net";
|
|
19
|
-
|
|
20
|
-
import ColumnResizer from "column-resizer";
|
|
21
|
-
const DEFAULT_PAGE_SIZE = 50;
|
|
22
|
-
|
|
23
|
-
export interface PluginConfig {
|
|
24
|
-
openIriInNewWindow: boolean;
|
|
25
|
-
tableConfig: Config;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface PersistentConfig {
|
|
29
|
-
pageSize?: number;
|
|
30
|
-
compact?: boolean;
|
|
31
|
-
isCompactView?: boolean;
|
|
32
|
-
showUriPrefixes?: boolean;
|
|
33
|
-
showDatatypes?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
type DataRow = [number, ...(Parser.BindingValue | "")[]];
|
|
37
|
-
|
|
38
|
-
function expand(this: HTMLDivElement, event: MouseEvent) {
|
|
39
|
-
addClass(this, "expanded");
|
|
40
|
-
event.preventDefault();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default class Table implements Plugin<PluginConfig> {
|
|
44
|
-
private config: DeepReadonly<PluginConfig>;
|
|
45
|
-
private persistentConfig: PersistentConfig = {};
|
|
46
|
-
private yasr: Yasr;
|
|
47
|
-
private tableControls: Element | undefined;
|
|
48
|
-
private tableEl: HTMLTableElement | undefined;
|
|
49
|
-
private dataTable: Api | undefined;
|
|
50
|
-
private tableFilterField: HTMLInputElement | undefined;
|
|
51
|
-
private tableSizeField: HTMLSelectElement | undefined;
|
|
52
|
-
private tableCompactSwitch: HTMLInputElement | undefined;
|
|
53
|
-
private tableCompactViewSwitch: HTMLInputElement | undefined;
|
|
54
|
-
private tableUriPrefixSwitch: HTMLInputElement | undefined;
|
|
55
|
-
private tableDatatypeSwitch: HTMLInputElement | undefined;
|
|
56
|
-
private tableResizer:
|
|
57
|
-
| {
|
|
58
|
-
reset: (options: {
|
|
59
|
-
disable: boolean;
|
|
60
|
-
onResize?: () => void;
|
|
61
|
-
partialRefresh?: boolean;
|
|
62
|
-
headerOnly?: boolean;
|
|
63
|
-
}) => void;
|
|
64
|
-
onResize: () => void;
|
|
65
|
-
}
|
|
66
|
-
| undefined;
|
|
67
|
-
public helpReference = "https://docs.triply.cc/yasgui/#table";
|
|
68
|
-
public label = "Table";
|
|
69
|
-
public priority = 10;
|
|
70
|
-
public getIcon() {
|
|
71
|
-
return drawSvgStringAsElement(drawFontAwesomeIconAsSvg(faTableIcon));
|
|
72
|
-
}
|
|
73
|
-
constructor(yasr: Yasr) {
|
|
74
|
-
this.yasr = yasr;
|
|
75
|
-
//TODO read options from constructor
|
|
76
|
-
this.config = Table.defaults;
|
|
77
|
-
}
|
|
78
|
-
public static defaults: PluginConfig = {
|
|
79
|
-
openIriInNewWindow: true,
|
|
80
|
-
tableConfig: {
|
|
81
|
-
layout: {
|
|
82
|
-
// @ts-ignore
|
|
83
|
-
top: null, // @TODO: remove ignore once https://github.com/DataTables/DataTablesSrc/issues/271 is released
|
|
84
|
-
// @ts-ignore
|
|
85
|
-
topStart: null, // @TODO: remove ignore once https://github.com/DataTables/DataTablesSrc/issues/271 is released
|
|
86
|
-
// @ts-ignore
|
|
87
|
-
topEnd: null, // @TODO: remove ignore once https://github.com/DataTables/DataTablesSrc/issues/271 is released
|
|
88
|
-
},
|
|
89
|
-
pageLength: DEFAULT_PAGE_SIZE, //default page length
|
|
90
|
-
lengthChange: true, //allow changing page length
|
|
91
|
-
data: [],
|
|
92
|
-
columns: [],
|
|
93
|
-
order: [],
|
|
94
|
-
deferRender: true,
|
|
95
|
-
orderClasses: false,
|
|
96
|
-
language: {
|
|
97
|
-
paginate: {
|
|
98
|
-
first: "<<", // Have to specify these two due to TS defs, <<
|
|
99
|
-
last: ">>", // Have to specify these two due to TS defs, >>
|
|
100
|
-
next: ">", // >
|
|
101
|
-
previous: "<", // <
|
|
102
|
-
},
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
private getRows(): DataRow[] {
|
|
107
|
-
if (!this.yasr.results) return [];
|
|
108
|
-
const bindings = this.yasr.results.getBindings();
|
|
109
|
-
if (!bindings) return [];
|
|
110
|
-
// Vars decide the columns
|
|
111
|
-
const vars = this.yasr.results.getVariables();
|
|
112
|
-
// Use "" as the empty value, undefined will throw runtime errors
|
|
113
|
-
return bindings.map((binding, rowId) => [rowId + 1, ...vars.map((variable) => binding[variable] ?? "")]);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private getMarkdownTable(): string {
|
|
117
|
-
if (!this.yasr.results) return "";
|
|
118
|
-
const bindings = this.yasr.results.getBindings();
|
|
119
|
-
if (!bindings) return "";
|
|
120
|
-
const vars = this.yasr.results.getVariables();
|
|
121
|
-
|
|
122
|
-
// Helper to escape special characters in markdown (backslashes and pipes)
|
|
123
|
-
const escapeMarkdown = (str: string) => str.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
124
|
-
|
|
125
|
-
// Helper to get plain text value from binding
|
|
126
|
-
const getPlainValue = (binding: Parser.BindingValue | ""): string => {
|
|
127
|
-
if (binding === "") return "";
|
|
128
|
-
return escapeMarkdown(binding.value);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Create header row
|
|
132
|
-
let markdown = "| " + vars.map(escapeMarkdown).join(" | ") + " |\n";
|
|
133
|
-
// Create separator row
|
|
134
|
-
markdown += "| " + vars.map(() => "---").join(" | ") + " |\n";
|
|
135
|
-
// Create data rows
|
|
136
|
-
bindings.forEach((binding) => {
|
|
137
|
-
const row = vars.map((variable) => getPlainValue(binding[variable] ?? ""));
|
|
138
|
-
markdown += "| " + row.join(" | ") + " |\n";
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
return markdown;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
private getUriLinkFromBinding(binding: Parser.BindingValue, prefixes?: { [key: string]: string }) {
|
|
145
|
-
const href = sanitize(binding.value);
|
|
146
|
-
let visibleString = href;
|
|
147
|
-
let prefixed = false;
|
|
148
|
-
// Apply URI prefixing if enabled (default true)
|
|
149
|
-
if (this.persistentConfig.showUriPrefixes !== false && prefixes) {
|
|
150
|
-
for (const prefixLabel in prefixes) {
|
|
151
|
-
if (visibleString.indexOf(prefixes[prefixLabel]) == 0) {
|
|
152
|
-
visibleString = prefixLabel + ":" + href.substring(prefixes[prefixLabel].length);
|
|
153
|
-
prefixed = true;
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// Hide brackets when prefixed or compact
|
|
159
|
-
const hideBrackets = prefixed || this.persistentConfig.compact;
|
|
160
|
-
const uri = `${hideBrackets ? "" : "<"}<a class='iri' target='${
|
|
161
|
-
this.config.openIriInNewWindow ? "_blank" : "_self"
|
|
162
|
-
}'${this.config.openIriInNewWindow ? " ref='noopener noreferrer'" : ""} href='${href}'>${sanitize(
|
|
163
|
-
visibleString,
|
|
164
|
-
)}</a>${hideBrackets ? "" : ">"}`;
|
|
165
|
-
return sanitize(uri);
|
|
166
|
-
}
|
|
167
|
-
private getCellContent(binding: Parser.BindingValue, prefixes?: { [label: string]: string }): string {
|
|
168
|
-
let content: string;
|
|
169
|
-
if (binding.type == "uri") {
|
|
170
|
-
content = `<span>${this.getUriLinkFromBinding(binding, prefixes)}</span>`;
|
|
171
|
-
} else {
|
|
172
|
-
content = `<span class='nonIri'>${this.formatLiteral(binding, prefixes)}</span>`;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return `<div>${content}</div>`;
|
|
176
|
-
}
|
|
177
|
-
private formatLiteral(literalBinding: Parser.BindingValue, prefixes?: { [key: string]: string }) {
|
|
178
|
-
let stringRepresentation = sanitize(escape(literalBinding.value));
|
|
179
|
-
// Return now when in compact mode.
|
|
180
|
-
if (this.persistentConfig.compact) return stringRepresentation;
|
|
181
|
-
|
|
182
|
-
// Show datatypes if enabled (default true)
|
|
183
|
-
if (this.persistentConfig.showDatatypes !== false) {
|
|
184
|
-
if (literalBinding["xml:lang"]) {
|
|
185
|
-
stringRepresentation = `"${stringRepresentation}"<sup>@${literalBinding["xml:lang"]}</sup>`;
|
|
186
|
-
} else if (literalBinding.datatype) {
|
|
187
|
-
const dataType = this.getUriLinkFromBinding({ type: "uri", value: literalBinding.datatype }, prefixes);
|
|
188
|
-
stringRepresentation = `"${stringRepresentation}"<sup>^^${dataType}</sup>`;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return stringRepresentation;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private getColumns(): ConfigColumns[] {
|
|
195
|
-
if (!this.yasr.results) return [];
|
|
196
|
-
const prefixes = this.yasr.getPrefixes();
|
|
197
|
-
|
|
198
|
-
return [
|
|
199
|
-
{
|
|
200
|
-
name: "",
|
|
201
|
-
searchable: false,
|
|
202
|
-
width: `${this.getSizeFirstColumn()}px`,
|
|
203
|
-
type: "num",
|
|
204
|
-
orderable: false,
|
|
205
|
-
visible: this.persistentConfig.compact !== true,
|
|
206
|
-
render: (data: number, type: any) =>
|
|
207
|
-
type === "filter" || type === "sort" || !type ? data : `<div class="rowNumber">${data}</div>`,
|
|
208
|
-
}, //prepend with row numbers column
|
|
209
|
-
...this.yasr.results?.getVariables().map((name) => {
|
|
210
|
-
return <ConfigColumns>{
|
|
211
|
-
name,
|
|
212
|
-
title: sanitize(name),
|
|
213
|
-
render: (data: Parser.BindingValue | "", type: any, _row: any, _meta: CellMetaSettings) => {
|
|
214
|
-
// Handle empty rows
|
|
215
|
-
if (data === "") return data;
|
|
216
|
-
if (type === "filter" || type === "sort" || !type) return sanitize(data.value);
|
|
217
|
-
return this.getCellContent(data, prefixes);
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
}),
|
|
221
|
-
];
|
|
222
|
-
}
|
|
223
|
-
private getSizeFirstColumn() {
|
|
224
|
-
const numResults = this.yasr.results?.getBindings()?.length || 0;
|
|
225
|
-
return numResults.toString().length * 8;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
public draw(persistentConfig: PersistentConfig) {
|
|
229
|
-
this.persistentConfig = { ...this.persistentConfig, ...persistentConfig };
|
|
230
|
-
this.tableEl = document.createElement("table");
|
|
231
|
-
const rows = this.getRows();
|
|
232
|
-
const columns = this.getColumns();
|
|
233
|
-
|
|
234
|
-
if (rows.length <= (persistentConfig?.pageSize || DEFAULT_PAGE_SIZE)) {
|
|
235
|
-
this.yasr.pluginControls;
|
|
236
|
-
addClass(this.yasr.rootEl, "isSinglePage");
|
|
237
|
-
} else {
|
|
238
|
-
removeClass(this.yasr.rootEl, "isSinglePage");
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (this.dataTable) {
|
|
242
|
-
this.destroyResizer();
|
|
243
|
-
|
|
244
|
-
this.dataTable.destroy(true);
|
|
245
|
-
this.dataTable = undefined;
|
|
246
|
-
}
|
|
247
|
-
this.yasr.resultsEl.appendChild(this.tableEl);
|
|
248
|
-
// reset some default config properties as they couldn't be initialized beforehand
|
|
249
|
-
const dtConfig: Config = {
|
|
250
|
-
...(cloneDeep(this.config.tableConfig) as unknown as Config),
|
|
251
|
-
pageLength: persistentConfig?.pageSize ? persistentConfig.pageSize : DEFAULT_PAGE_SIZE,
|
|
252
|
-
data: rows,
|
|
253
|
-
columns: columns,
|
|
254
|
-
};
|
|
255
|
-
this.dataTable = $(this.tableEl).DataTable(dtConfig);
|
|
256
|
-
this.tableEl.style.removeProperty("width");
|
|
257
|
-
this.tableEl.style.width = this.tableEl.clientWidth + "px";
|
|
258
|
-
const widths = Array.from(this.tableEl.querySelectorAll("th")).map((h) => h.offsetWidth - 26);
|
|
259
|
-
this.tableResizer = new ColumnResizer(this.tableEl, {
|
|
260
|
-
widths: this.persistentConfig.compact === true ? widths : [this.getSizeFirstColumn(), ...widths.slice(1)],
|
|
261
|
-
partialRefresh: true,
|
|
262
|
-
onResize: this.persistentConfig.isCompactView !== false && this.setEllipsisHandlers,
|
|
263
|
-
headerOnly: true,
|
|
264
|
-
});
|
|
265
|
-
// DataTables uses the rendered style to decide the widths of columns.
|
|
266
|
-
// Before a draw remove the compactTable styling
|
|
267
|
-
if (this.persistentConfig.isCompactView !== false) {
|
|
268
|
-
this.dataTable?.on("preDraw", () => {
|
|
269
|
-
this.tableResizer?.reset({ disable: true });
|
|
270
|
-
removeClass(this.tableEl, "compactTable");
|
|
271
|
-
this.tableEl?.style.removeProperty("width");
|
|
272
|
-
this.tableEl?.style.setProperty("width", this.tableEl.clientWidth + "px");
|
|
273
|
-
return true; // Indicate it should re-render
|
|
274
|
-
});
|
|
275
|
-
// After a draw
|
|
276
|
-
this.dataTable?.on("draw", () => {
|
|
277
|
-
if (!this.tableEl) return;
|
|
278
|
-
// Width of table after render, removing width will make it fall back to 100%
|
|
279
|
-
let targetSize = this.tableEl.clientWidth;
|
|
280
|
-
this.tableEl.style.removeProperty("width");
|
|
281
|
-
// Let's make sure the new size is not bigger
|
|
282
|
-
if (targetSize > this.tableEl.clientWidth) targetSize = this.tableEl.clientWidth;
|
|
283
|
-
this.tableEl?.style.setProperty("width", `${targetSize}px`);
|
|
284
|
-
// Enable the re-sizer
|
|
285
|
-
this.tableResizer?.reset({
|
|
286
|
-
disable: false,
|
|
287
|
-
partialRefresh: true,
|
|
288
|
-
onResize: this.setEllipsisHandlers,
|
|
289
|
-
headerOnly: true,
|
|
290
|
-
});
|
|
291
|
-
// Re-add the compact styling
|
|
292
|
-
addClass(this.tableEl, "compactTable");
|
|
293
|
-
// Check if cells need the ellipsisHandlers
|
|
294
|
-
this.setEllipsisHandlers();
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
this.drawControls();
|
|
299
|
-
// Draw again but with the events
|
|
300
|
-
if (this.persistentConfig.isCompactView !== false) {
|
|
301
|
-
addClass(this.tableEl, "compactTable");
|
|
302
|
-
this.setEllipsisHandlers();
|
|
303
|
-
}
|
|
304
|
-
// if (this.tableEl.clientWidth > width) this.tableEl.parentElement?.style.setProperty("overflow", "hidden");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
private setEllipsisHandlers = () => {
|
|
308
|
-
this.dataTable?.cells({ page: "current" }).every((rowIdx, colIdx) => {
|
|
309
|
-
const cell = this.dataTable?.cell(rowIdx, colIdx);
|
|
310
|
-
if (cell?.data() === "") return;
|
|
311
|
-
const cellNode = cell?.node() as HTMLTableCellElement;
|
|
312
|
-
if (cellNode) {
|
|
313
|
-
const content = cellNode.firstChild as HTMLDivElement;
|
|
314
|
-
if ((content.firstElementChild?.getBoundingClientRect().width || 0) > content.getBoundingClientRect().width) {
|
|
315
|
-
if (!content.classList.contains("expandable")) {
|
|
316
|
-
addClass(content, "expandable");
|
|
317
|
-
content.addEventListener("click", expand, { once: true });
|
|
318
|
-
}
|
|
319
|
-
} else {
|
|
320
|
-
if (content.classList.contains("expandable")) {
|
|
321
|
-
removeClass(content, "expandable");
|
|
322
|
-
content.removeEventListener("click", expand);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
};
|
|
328
|
-
private handleTableSearch = (event: KeyboardEvent) => {
|
|
329
|
-
this.dataTable?.search((event.target as HTMLInputElement).value).draw("page");
|
|
330
|
-
};
|
|
331
|
-
private handleTableSizeSelect = (event: Event) => {
|
|
332
|
-
const pageLength = parseInt((event.target as HTMLSelectElement).value);
|
|
333
|
-
// Set page length
|
|
334
|
-
this.dataTable?.page.len(pageLength).draw("page");
|
|
335
|
-
// Store in persistentConfig
|
|
336
|
-
this.persistentConfig.pageSize = pageLength;
|
|
337
|
-
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
338
|
-
};
|
|
339
|
-
private handleSetCompactToggle = (event: Event) => {
|
|
340
|
-
// Store in persistentConfig
|
|
341
|
-
this.persistentConfig.compact = (event.target as HTMLInputElement).checked;
|
|
342
|
-
// Update the table
|
|
343
|
-
this.draw(this.persistentConfig);
|
|
344
|
-
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
345
|
-
};
|
|
346
|
-
private handleSetCompactViewToggle = (event: Event) => {
|
|
347
|
-
// Store in persistentConfig
|
|
348
|
-
this.persistentConfig.isCompactView = (event.target as HTMLInputElement).checked;
|
|
349
|
-
// Update the table
|
|
350
|
-
this.draw(this.persistentConfig);
|
|
351
|
-
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
352
|
-
};
|
|
353
|
-
private handleSetUriPrefixToggle = (event: Event) => {
|
|
354
|
-
// Store in persistentConfig
|
|
355
|
-
this.persistentConfig.showUriPrefixes = (event.target as HTMLInputElement).checked;
|
|
356
|
-
// Update the table
|
|
357
|
-
this.draw(this.persistentConfig);
|
|
358
|
-
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
359
|
-
};
|
|
360
|
-
private handleSetDatatypeToggle = (event: Event) => {
|
|
361
|
-
// Store in persistentConfig
|
|
362
|
-
this.persistentConfig.showDatatypes = (event.target as HTMLInputElement).checked;
|
|
363
|
-
// Update the table
|
|
364
|
-
this.draw(this.persistentConfig);
|
|
365
|
-
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
366
|
-
};
|
|
367
|
-
private handleCopyMarkdown = async (event: Event) => {
|
|
368
|
-
const markdown = this.getMarkdownTable();
|
|
369
|
-
const button = event.target as HTMLButtonElement;
|
|
370
|
-
|
|
371
|
-
// Prevent multiple rapid clicks
|
|
372
|
-
if (button.disabled) return;
|
|
373
|
-
button.disabled = true;
|
|
374
|
-
|
|
375
|
-
const originalText = "Copy as Markdown";
|
|
376
|
-
try {
|
|
377
|
-
await navigator.clipboard.writeText(markdown);
|
|
378
|
-
// Provide visual feedback
|
|
379
|
-
button.textContent = "Copied!";
|
|
380
|
-
setTimeout(() => {
|
|
381
|
-
button.textContent = originalText;
|
|
382
|
-
button.disabled = false;
|
|
383
|
-
}, 2000);
|
|
384
|
-
} catch (err) {
|
|
385
|
-
// Show user-friendly error
|
|
386
|
-
button.textContent = "Copy failed";
|
|
387
|
-
setTimeout(() => {
|
|
388
|
-
button.textContent = originalText;
|
|
389
|
-
button.disabled = false;
|
|
390
|
-
}, 2000);
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
/**
|
|
394
|
-
* Draws controls on each update
|
|
395
|
-
*/
|
|
396
|
-
drawControls() {
|
|
397
|
-
// Remove old header
|
|
398
|
-
this.removeControls();
|
|
399
|
-
this.tableControls = document.createElement("div");
|
|
400
|
-
this.tableControls.className = "tableControls";
|
|
401
|
-
|
|
402
|
-
// Compact switch
|
|
403
|
-
const toggleWrapper = document.createElement("div");
|
|
404
|
-
const switchComponent = document.createElement("label");
|
|
405
|
-
const textComponent = document.createElement("span");
|
|
406
|
-
textComponent.innerText = "Simple view";
|
|
407
|
-
addClass(textComponent, "label");
|
|
408
|
-
switchComponent.appendChild(textComponent);
|
|
409
|
-
addClass(switchComponent, "switch");
|
|
410
|
-
toggleWrapper.appendChild(switchComponent);
|
|
411
|
-
this.tableCompactSwitch = document.createElement("input");
|
|
412
|
-
switchComponent.addEventListener("change", this.handleSetCompactToggle);
|
|
413
|
-
this.tableCompactSwitch.type = "checkbox";
|
|
414
|
-
switchComponent.appendChild(this.tableCompactSwitch);
|
|
415
|
-
this.tableCompactSwitch.defaultChecked = !!this.persistentConfig.compact;
|
|
416
|
-
this.tableControls.appendChild(toggleWrapper);
|
|
417
|
-
|
|
418
|
-
// Compact view switch
|
|
419
|
-
const compactViewToggleWrapper = document.createElement("div");
|
|
420
|
-
const compactViewSwitchComponent = document.createElement("label");
|
|
421
|
-
const compactViewTextComponent = document.createElement("span");
|
|
422
|
-
compactViewTextComponent.innerText = "Compact";
|
|
423
|
-
addClass(compactViewTextComponent, "label");
|
|
424
|
-
compactViewSwitchComponent.appendChild(compactViewTextComponent);
|
|
425
|
-
addClass(compactViewSwitchComponent, "switch");
|
|
426
|
-
compactViewToggleWrapper.appendChild(compactViewSwitchComponent);
|
|
427
|
-
this.tableCompactViewSwitch = document.createElement("input");
|
|
428
|
-
compactViewSwitchComponent.addEventListener("change", this.handleSetCompactViewToggle);
|
|
429
|
-
this.tableCompactViewSwitch.type = "checkbox";
|
|
430
|
-
compactViewSwitchComponent.appendChild(this.tableCompactViewSwitch);
|
|
431
|
-
this.tableCompactViewSwitch.defaultChecked = this.persistentConfig.isCompactView !== false;
|
|
432
|
-
this.tableControls.appendChild(compactViewToggleWrapper);
|
|
433
|
-
|
|
434
|
-
// URI Prefix switch
|
|
435
|
-
const uriPrefixToggleWrapper = document.createElement("div");
|
|
436
|
-
const uriPrefixSwitchComponent = document.createElement("label");
|
|
437
|
-
const uriPrefixTextComponent = document.createElement("span");
|
|
438
|
-
uriPrefixTextComponent.innerText = "Prefixes";
|
|
439
|
-
addClass(uriPrefixTextComponent, "label");
|
|
440
|
-
uriPrefixSwitchComponent.appendChild(uriPrefixTextComponent);
|
|
441
|
-
addClass(uriPrefixSwitchComponent, "switch");
|
|
442
|
-
uriPrefixToggleWrapper.appendChild(uriPrefixSwitchComponent);
|
|
443
|
-
this.tableUriPrefixSwitch = document.createElement("input");
|
|
444
|
-
uriPrefixSwitchComponent.addEventListener("change", this.handleSetUriPrefixToggle);
|
|
445
|
-
this.tableUriPrefixSwitch.type = "checkbox";
|
|
446
|
-
uriPrefixSwitchComponent.appendChild(this.tableUriPrefixSwitch);
|
|
447
|
-
this.tableUriPrefixSwitch.defaultChecked = this.persistentConfig.showUriPrefixes !== false;
|
|
448
|
-
this.tableControls.appendChild(uriPrefixToggleWrapper);
|
|
449
|
-
|
|
450
|
-
// Datatype switch
|
|
451
|
-
const datatypeToggleWrapper = document.createElement("div");
|
|
452
|
-
const datatypeSwitchComponent = document.createElement("label");
|
|
453
|
-
const datatypeTextComponent = document.createElement("span");
|
|
454
|
-
datatypeTextComponent.innerText = "Datatypes";
|
|
455
|
-
addClass(datatypeTextComponent, "label");
|
|
456
|
-
datatypeSwitchComponent.appendChild(datatypeTextComponent);
|
|
457
|
-
addClass(datatypeSwitchComponent, "switch");
|
|
458
|
-
datatypeToggleWrapper.appendChild(datatypeSwitchComponent);
|
|
459
|
-
this.tableDatatypeSwitch = document.createElement("input");
|
|
460
|
-
datatypeSwitchComponent.addEventListener("change", this.handleSetDatatypeToggle);
|
|
461
|
-
this.tableDatatypeSwitch.type = "checkbox";
|
|
462
|
-
datatypeSwitchComponent.appendChild(this.tableDatatypeSwitch);
|
|
463
|
-
this.tableDatatypeSwitch.defaultChecked = this.persistentConfig.showDatatypes !== false;
|
|
464
|
-
this.tableControls.appendChild(datatypeToggleWrapper);
|
|
465
|
-
|
|
466
|
-
// Create table filter
|
|
467
|
-
this.tableFilterField = document.createElement("input");
|
|
468
|
-
this.tableFilterField.className = "tableFilter";
|
|
469
|
-
this.tableFilterField.placeholder = "Filter query results";
|
|
470
|
-
this.tableFilterField.setAttribute("aria-label", "Filter query results");
|
|
471
|
-
this.tableControls.appendChild(this.tableFilterField);
|
|
472
|
-
this.tableFilterField.addEventListener("keyup", this.handleTableSearch);
|
|
473
|
-
|
|
474
|
-
// Create markdown copy button
|
|
475
|
-
const markdownButton = document.createElement("button");
|
|
476
|
-
markdownButton.className = "copyMarkdownBtn";
|
|
477
|
-
markdownButton.textContent = "Copy as Markdown";
|
|
478
|
-
markdownButton.setAttribute("aria-label", "Copy table as markdown");
|
|
479
|
-
markdownButton.addEventListener("click", this.handleCopyMarkdown);
|
|
480
|
-
this.tableControls.appendChild(markdownButton);
|
|
481
|
-
|
|
482
|
-
// Create page wrapper
|
|
483
|
-
const pageSizerWrapper = document.createElement("div");
|
|
484
|
-
pageSizerWrapper.className = "pageSizeWrapper";
|
|
485
|
-
|
|
486
|
-
// Create label for page size element
|
|
487
|
-
const pageSizerLabel = document.createElement("span");
|
|
488
|
-
pageSizerLabel.textContent = "Page size: ";
|
|
489
|
-
pageSizerLabel.className = "pageSizerLabel";
|
|
490
|
-
pageSizerWrapper.appendChild(pageSizerLabel);
|
|
491
|
-
|
|
492
|
-
// Create page size element
|
|
493
|
-
this.tableSizeField = document.createElement("select");
|
|
494
|
-
this.tableSizeField.className = "tableSizer";
|
|
495
|
-
|
|
496
|
-
// Create options for page sizer
|
|
497
|
-
const options = [10, 50, 100, 1000, -1];
|
|
498
|
-
for (const option of options) {
|
|
499
|
-
const element = document.createElement("option");
|
|
500
|
-
element.value = option + "";
|
|
501
|
-
// -1 selects everything so we should call it All
|
|
502
|
-
element.innerText = option > 0 ? option + "" : "All";
|
|
503
|
-
// Set initial one as selected
|
|
504
|
-
if (this.dataTable?.page.len() === option) element.selected = true;
|
|
505
|
-
this.tableSizeField.appendChild(element);
|
|
506
|
-
}
|
|
507
|
-
pageSizerWrapper.appendChild(this.tableSizeField);
|
|
508
|
-
this.tableSizeField.addEventListener("change", this.handleTableSizeSelect);
|
|
509
|
-
this.tableControls.appendChild(pageSizerWrapper);
|
|
510
|
-
this.yasr.pluginControls.appendChild(this.tableControls);
|
|
511
|
-
}
|
|
512
|
-
download(filename?: string) {
|
|
513
|
-
return {
|
|
514
|
-
getData: () => this.yasr.results?.asCsv() || "",
|
|
515
|
-
contentType: "text/csv",
|
|
516
|
-
title: "Download result",
|
|
517
|
-
filename: `${filename || "queryResults"}.csv`,
|
|
518
|
-
} as DownloadInfo;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
public canHandleResults() {
|
|
522
|
-
return !!this.yasr.results && this.yasr.results.getVariables() && this.yasr.results.getVariables().length > 0;
|
|
523
|
-
}
|
|
524
|
-
private removeControls() {
|
|
525
|
-
// Unregister listeners and remove references to old fields
|
|
526
|
-
this.tableFilterField?.removeEventListener("keyup", this.handleTableSearch);
|
|
527
|
-
this.tableFilterField = undefined;
|
|
528
|
-
this.tableSizeField?.removeEventListener("change", this.handleTableSizeSelect);
|
|
529
|
-
this.tableSizeField = undefined;
|
|
530
|
-
this.tableCompactSwitch?.removeEventListener("change", this.handleSetCompactToggle);
|
|
531
|
-
this.tableCompactSwitch = undefined;
|
|
532
|
-
this.tableCompactViewSwitch?.removeEventListener("change", this.handleSetCompactViewToggle);
|
|
533
|
-
this.tableCompactViewSwitch = undefined;
|
|
534
|
-
this.tableUriPrefixSwitch?.removeEventListener("change", this.handleSetUriPrefixToggle);
|
|
535
|
-
this.tableUriPrefixSwitch = undefined;
|
|
536
|
-
this.tableDatatypeSwitch?.removeEventListener("change", this.handleSetDatatypeToggle);
|
|
537
|
-
this.tableDatatypeSwitch = undefined;
|
|
538
|
-
// Empty controls
|
|
539
|
-
while (this.tableControls?.firstChild) this.tableControls.firstChild.remove();
|
|
540
|
-
this.tableControls?.remove();
|
|
541
|
-
}
|
|
542
|
-
private destroyResizer() {
|
|
543
|
-
if (this.tableResizer) {
|
|
544
|
-
this.tableResizer.reset({ disable: true });
|
|
545
|
-
window.removeEventListener("resize", this.tableResizer.onResize);
|
|
546
|
-
this.tableResizer = undefined;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
destroy() {
|
|
550
|
-
this.removeControls();
|
|
551
|
-
this.destroyResizer();
|
|
552
|
-
// According to datatables docs, destroy(true) will also remove all events
|
|
553
|
-
this.dataTable?.destroy(true);
|
|
554
|
-
this.dataTable = undefined;
|
|
555
|
-
removeClass(this.yasr.rootEl, "isSinglePage");
|
|
556
|
-
}
|
|
557
|
-
}
|