@matdata/yasr 5.3.0 → 5.4.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/plugins/table/__tests__/table-test.d.ts +1 -0
- package/build/ts/src/plugins/table/__tests__/table-test.js +190 -0
- package/build/ts/src/plugins/table/__tests__/table-test.js.map +1 -0
- package/build/ts/src/plugins/table/index.d.ts +11 -3
- package/build/ts/src/plugins/table/index.js +137 -34
- package/build/ts/src/plugins/table/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 +35 -32
- package/build/yasr.min.js.map +3 -3
- package/package.json +1 -1
- package/src/bin/takeScreenshot.js +5 -6
- package/src/plugins/table/__tests__/table-test.ts +246 -0
- package/src/plugins/table/index.scss +32 -19
- package/src/plugins/table/index.ts +155 -35
package/package.json
CHANGED
|
@@ -227,10 +227,9 @@ const getHtml = (plugin) => `
|
|
|
227
227
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.0.0/polyfill.js"></script>
|
|
228
228
|
<style>
|
|
229
229
|
body {
|
|
230
|
-
font-family:
|
|
230
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
|
231
231
|
}
|
|
232
232
|
</style>
|
|
233
|
-
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" />
|
|
234
233
|
<link rel="stylesheet" href="build/yasr.min.css" />
|
|
235
234
|
<link rel="stylesheet" href="build/pro-gallery.min.css">
|
|
236
235
|
<link rel="stylesheet" href="build/pro-geo.min.css">
|
|
@@ -302,7 +301,7 @@ const getScreenWidth = (plugin) => {
|
|
|
302
301
|
};
|
|
303
302
|
|
|
304
303
|
let staticFileServer = new static.Server("./");
|
|
305
|
-
function setupServer
|
|
304
|
+
function setupServer() {
|
|
306
305
|
return new Promise((resolve, reject) => {
|
|
307
306
|
var server = http
|
|
308
307
|
.createServer(function (request, response) {
|
|
@@ -319,10 +318,10 @@ function setupServer () {
|
|
|
319
318
|
.on("error", (e) => reject(e));
|
|
320
319
|
});
|
|
321
320
|
}
|
|
322
|
-
function wait
|
|
321
|
+
function wait(time) {
|
|
323
322
|
return new Promise((resolve) => setTimeout(resolve, time));
|
|
324
323
|
}
|
|
325
|
-
function waitForImagesToLoad
|
|
324
|
+
function waitForImagesToLoad(page) {
|
|
326
325
|
return page.evaluate(() => {
|
|
327
326
|
const selectors = Array.from(document.querySelectorAll("img"));
|
|
328
327
|
return Promise.all(
|
|
@@ -332,7 +331,7 @@ function waitForImagesToLoad (page) {
|
|
|
332
331
|
img.addEventListener("load", resolve);
|
|
333
332
|
img.addEventListener("error", reject);
|
|
334
333
|
});
|
|
335
|
-
})
|
|
334
|
+
}),
|
|
336
335
|
);
|
|
337
336
|
});
|
|
338
337
|
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { describe, it } from "mocha";
|
|
2
|
+
import { expect } from "chai";
|
|
3
|
+
import Yasr from "../../../index";
|
|
4
|
+
import Parser from "../../../parsers";
|
|
5
|
+
|
|
6
|
+
describe("Table Plugin", () => {
|
|
7
|
+
describe("Markdown Generation", () => {
|
|
8
|
+
it("should generate markdown table from simple results", () => {
|
|
9
|
+
const container = document.createElement("div");
|
|
10
|
+
const yasr = new Yasr(container);
|
|
11
|
+
|
|
12
|
+
// Mock SPARQL results
|
|
13
|
+
const mockResults = {
|
|
14
|
+
head: {
|
|
15
|
+
vars: ["subject", "predicate", "object"],
|
|
16
|
+
},
|
|
17
|
+
results: {
|
|
18
|
+
bindings: [
|
|
19
|
+
{
|
|
20
|
+
subject: { type: "uri", value: "http://example.org/resource/1" } as Parser.BindingValue,
|
|
21
|
+
predicate: { type: "uri", value: "http://example.org/name" } as Parser.BindingValue,
|
|
22
|
+
object: { type: "literal", value: "Test Name" } as Parser.BindingValue,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
subject: { type: "uri", value: "http://example.org/resource/2" } as Parser.BindingValue,
|
|
26
|
+
predicate: { type: "uri", value: "http://example.org/name" } as Parser.BindingValue,
|
|
27
|
+
object: { type: "literal", value: "Another Name" } as Parser.BindingValue,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
yasr.setResponse(
|
|
34
|
+
{
|
|
35
|
+
response: JSON.stringify(mockResults),
|
|
36
|
+
status: 200,
|
|
37
|
+
contentType: "application/sparql-results+json",
|
|
38
|
+
},
|
|
39
|
+
0,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const tablePlugin = yasr.plugins["table"];
|
|
43
|
+
// @ts-ignore - accessing private method for testing
|
|
44
|
+
const markdown = tablePlugin.getMarkdownTable();
|
|
45
|
+
|
|
46
|
+
expect(markdown).to.be.a("string");
|
|
47
|
+
expect(markdown).to.include("| subject | predicate | object |");
|
|
48
|
+
expect(markdown).to.include("| --- | --- | --- |");
|
|
49
|
+
expect(markdown).to.include("http://example.org/resource/1");
|
|
50
|
+
expect(markdown).to.include("Test Name");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should escape pipe characters in markdown values", () => {
|
|
54
|
+
const container = document.createElement("div");
|
|
55
|
+
const yasr = new Yasr(container);
|
|
56
|
+
|
|
57
|
+
// Mock SPARQL results with pipe character
|
|
58
|
+
const mockResults = {
|
|
59
|
+
head: {
|
|
60
|
+
vars: ["value"],
|
|
61
|
+
},
|
|
62
|
+
results: {
|
|
63
|
+
bindings: [
|
|
64
|
+
{
|
|
65
|
+
value: { type: "literal", value: "text|with|pipes" } as Parser.BindingValue,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
yasr.setResponse(
|
|
72
|
+
{
|
|
73
|
+
response: JSON.stringify(mockResults),
|
|
74
|
+
status: 200,
|
|
75
|
+
contentType: "application/sparql-results+json",
|
|
76
|
+
},
|
|
77
|
+
0,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const tablePlugin = yasr.plugins["table"];
|
|
81
|
+
// @ts-ignore - accessing private method for testing
|
|
82
|
+
const markdown = tablePlugin.getMarkdownTable();
|
|
83
|
+
|
|
84
|
+
expect(markdown).to.include("text\\|with\\|pipes");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("URI Prefixing", () => {
|
|
89
|
+
it("should show URI prefixes when enabled", () => {
|
|
90
|
+
const container = document.createElement("div");
|
|
91
|
+
const yasr = new Yasr(container, {
|
|
92
|
+
prefixes: {
|
|
93
|
+
ex: "http://example.org/",
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const mockResults = {
|
|
98
|
+
head: {
|
|
99
|
+
vars: ["resource"],
|
|
100
|
+
},
|
|
101
|
+
results: {
|
|
102
|
+
bindings: [
|
|
103
|
+
{
|
|
104
|
+
resource: { type: "uri", value: "http://example.org/test" } as Parser.BindingValue,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
yasr.setResponse(
|
|
111
|
+
{
|
|
112
|
+
response: JSON.stringify(mockResults),
|
|
113
|
+
status: 200,
|
|
114
|
+
contentType: "application/sparql-results+json",
|
|
115
|
+
},
|
|
116
|
+
0,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const tablePlugin = yasr.plugins["table"];
|
|
120
|
+
// Draw with prefixes enabled (default)
|
|
121
|
+
tablePlugin.draw({ showUriPrefixes: true });
|
|
122
|
+
|
|
123
|
+
const tableHtml = container.innerHTML;
|
|
124
|
+
expect(tableHtml).to.include("ex:test");
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should show full URIs when prefixing disabled", () => {
|
|
128
|
+
const container = document.createElement("div");
|
|
129
|
+
const yasr = new Yasr(container, {
|
|
130
|
+
prefixes: {
|
|
131
|
+
ex: "http://example.org/",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const mockResults = {
|
|
136
|
+
head: {
|
|
137
|
+
vars: ["resource"],
|
|
138
|
+
},
|
|
139
|
+
results: {
|
|
140
|
+
bindings: [
|
|
141
|
+
{
|
|
142
|
+
resource: { type: "uri", value: "http://example.org/test" } as Parser.BindingValue,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
yasr.setResponse(
|
|
149
|
+
{
|
|
150
|
+
response: JSON.stringify(mockResults),
|
|
151
|
+
status: 200,
|
|
152
|
+
contentType: "application/sparql-results+json",
|
|
153
|
+
},
|
|
154
|
+
0,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const tablePlugin = yasr.plugins["table"];
|
|
158
|
+
// Draw with prefixes disabled
|
|
159
|
+
tablePlugin.draw({ showUriPrefixes: false });
|
|
160
|
+
|
|
161
|
+
const tableHtml = container.innerHTML;
|
|
162
|
+
expect(tableHtml).to.include("http://example.org/test");
|
|
163
|
+
expect(tableHtml).to.not.include("ex:test");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("Datatype Display", () => {
|
|
168
|
+
it("should show datatypes when enabled", () => {
|
|
169
|
+
const container = document.createElement("div");
|
|
170
|
+
const yasr = new Yasr(container);
|
|
171
|
+
|
|
172
|
+
const mockResults = {
|
|
173
|
+
head: {
|
|
174
|
+
vars: ["number"],
|
|
175
|
+
},
|
|
176
|
+
results: {
|
|
177
|
+
bindings: [
|
|
178
|
+
{
|
|
179
|
+
number: {
|
|
180
|
+
type: "literal",
|
|
181
|
+
value: "42",
|
|
182
|
+
datatype: "http://www.w3.org/2001/XMLSchema#integer",
|
|
183
|
+
} as Parser.BindingValue,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
yasr.setResponse(
|
|
190
|
+
{
|
|
191
|
+
response: JSON.stringify(mockResults),
|
|
192
|
+
status: 200,
|
|
193
|
+
contentType: "application/sparql-results+json",
|
|
194
|
+
},
|
|
195
|
+
0,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const tablePlugin = yasr.plugins["table"];
|
|
199
|
+
// Draw with datatypes enabled (default)
|
|
200
|
+
tablePlugin.draw({ showDatatypes: true });
|
|
201
|
+
|
|
202
|
+
const tableHtml = container.innerHTML;
|
|
203
|
+
expect(tableHtml).to.include("^^");
|
|
204
|
+
expect(tableHtml).to.include("XMLSchema#integer");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should hide datatypes when disabled", () => {
|
|
208
|
+
const container = document.createElement("div");
|
|
209
|
+
const yasr = new Yasr(container);
|
|
210
|
+
|
|
211
|
+
const mockResults = {
|
|
212
|
+
head: {
|
|
213
|
+
vars: ["number"],
|
|
214
|
+
},
|
|
215
|
+
results: {
|
|
216
|
+
bindings: [
|
|
217
|
+
{
|
|
218
|
+
number: {
|
|
219
|
+
type: "literal",
|
|
220
|
+
value: "42",
|
|
221
|
+
datatype: "http://www.w3.org/2001/XMLSchema#integer",
|
|
222
|
+
} as Parser.BindingValue,
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
yasr.setResponse(
|
|
229
|
+
{
|
|
230
|
+
response: JSON.stringify(mockResults),
|
|
231
|
+
status: 200,
|
|
232
|
+
contentType: "application/sparql-results+json",
|
|
233
|
+
},
|
|
234
|
+
0,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const tablePlugin = yasr.plugins["table"];
|
|
238
|
+
// Draw with datatypes disabled
|
|
239
|
+
tablePlugin.draw({ showDatatypes: false });
|
|
240
|
+
|
|
241
|
+
const tableHtml = container.innerHTML;
|
|
242
|
+
expect(tableHtml).to.not.include("^^");
|
|
243
|
+
expect(tableHtml).to.not.include("XMLSchema#integer");
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -28,15 +28,34 @@ Delimiters used by datatables
|
|
|
28
28
|
align-items: center;
|
|
29
29
|
margin-right: 10px;
|
|
30
30
|
}
|
|
31
|
+
.copyMarkdownBtn {
|
|
32
|
+
margin-right: 10px;
|
|
33
|
+
padding: 4px 8px;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
border: 1px solid var(--yasgui-border-color, #ddd);
|
|
36
|
+
background-color: var(--yasgui-bg-secondary, #f7f7f7);
|
|
37
|
+
color: var(--yasgui-text-primary, #000);
|
|
38
|
+
border-radius: 3px;
|
|
39
|
+
font-size: 0.9em;
|
|
40
|
+
white-space: nowrap;
|
|
41
|
+
|
|
42
|
+
&:hover {
|
|
43
|
+
background-color: var(--yasgui-bg-tertiary, #eee);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:active {
|
|
47
|
+
background-color: var(--yasgui-bg-primary, #fff);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
31
50
|
}
|
|
32
|
-
.dataTable.
|
|
51
|
+
.dataTable.compactTable {
|
|
33
52
|
white-space: nowrap;
|
|
34
53
|
div:not(.expanded) {
|
|
35
54
|
overflow: hidden;
|
|
36
55
|
text-overflow: ellipsis;
|
|
37
56
|
}
|
|
38
57
|
}
|
|
39
|
-
.dataTable:not(.
|
|
58
|
+
.dataTable:not(.compactTable) {
|
|
40
59
|
div:not(.expanded) {
|
|
41
60
|
word-break: break-all;
|
|
42
61
|
}
|
|
@@ -52,7 +71,7 @@ Delimiters used by datatables
|
|
|
52
71
|
}
|
|
53
72
|
}
|
|
54
73
|
|
|
55
|
-
.
|
|
74
|
+
.dt-container {
|
|
56
75
|
font-size: 0.9em;
|
|
57
76
|
min-width: 100%;
|
|
58
77
|
.grip-container {
|
|
@@ -72,7 +91,7 @@ Delimiters used by datatables
|
|
|
72
91
|
}
|
|
73
92
|
|
|
74
93
|
tbody tr:hover {
|
|
75
|
-
background-color: #f9f9f9;
|
|
94
|
+
background-color: var(--yasgui-bg-tertiary, #f9f9f9);
|
|
76
95
|
}
|
|
77
96
|
|
|
78
97
|
thead tr th {
|
|
@@ -92,7 +111,7 @@ Delimiters used by datatables
|
|
|
92
111
|
padding-right: 18px; //space for sort icon
|
|
93
112
|
}
|
|
94
113
|
&:hover {
|
|
95
|
-
background-color: #f9f9f9;
|
|
114
|
+
background-color: var(--yasgui-tab-bg, #f9f9f9);
|
|
96
115
|
}
|
|
97
116
|
}
|
|
98
117
|
|
|
@@ -119,35 +138,29 @@ Delimiters used by datatables
|
|
|
119
138
|
/**
|
|
120
139
|
Selector for pagination element
|
|
121
140
|
*/
|
|
122
|
-
div.
|
|
123
|
-
|
|
141
|
+
div.dt-paging {
|
|
142
|
+
button.dt-paging-button {
|
|
124
143
|
border: none;
|
|
125
144
|
background: transparent;
|
|
145
|
+
color: var(--yasgui-text-secondary, #505050);
|
|
126
146
|
|
|
127
147
|
// When the buttons are disabled show the default YASR disabled color
|
|
128
|
-
|
|
129
|
-
color: #
|
|
148
|
+
&.disabled {
|
|
149
|
+
color: var(--yasgui-text-muted, #999) !important;
|
|
130
150
|
}
|
|
131
151
|
|
|
132
152
|
&.current {
|
|
133
|
-
border: none;
|
|
134
|
-
background: transparent;
|
|
135
153
|
text-decoration: underline !important; // dataTables made the text-decoration important
|
|
154
|
+
color: var(--yasgui-accent-color, #337ab7);
|
|
136
155
|
}
|
|
137
156
|
|
|
138
157
|
&:hover {
|
|
139
|
-
border: none;
|
|
140
|
-
background: transparent;
|
|
141
|
-
|
|
142
158
|
// Don't override the disabled grayed out style
|
|
143
159
|
&:not(.disabled) {
|
|
144
|
-
|
|
160
|
+
font-weight: bold;
|
|
161
|
+
color: var(--yasgui-text-secondary, #505050) !important;
|
|
145
162
|
}
|
|
146
163
|
}
|
|
147
|
-
|
|
148
|
-
&:active {
|
|
149
|
-
box-shadow: none;
|
|
150
|
-
}
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
166
|
}
|
|
@@ -28,7 +28,9 @@ export interface PluginConfig {
|
|
|
28
28
|
export interface PersistentConfig {
|
|
29
29
|
pageSize?: number;
|
|
30
30
|
compact?: boolean;
|
|
31
|
-
|
|
31
|
+
isCompactView?: boolean;
|
|
32
|
+
showUriPrefixes?: boolean;
|
|
33
|
+
showDatatypes?: boolean;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
type DataRow = [number, ...(Parser.BindingValue | "")[]];
|
|
@@ -48,7 +50,9 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
48
50
|
private tableFilterField: HTMLInputElement | undefined;
|
|
49
51
|
private tableSizeField: HTMLSelectElement | undefined;
|
|
50
52
|
private tableCompactSwitch: HTMLInputElement | undefined;
|
|
51
|
-
private
|
|
53
|
+
private tableCompactViewSwitch: HTMLInputElement | undefined;
|
|
54
|
+
private tableUriPrefixSwitch: HTMLInputElement | undefined;
|
|
55
|
+
private tableDatatypeSwitch: HTMLInputElement | undefined;
|
|
52
56
|
private tableResizer:
|
|
53
57
|
| {
|
|
54
58
|
reset: (options: {
|
|
@@ -109,11 +113,40 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
109
113
|
return bindings.map((binding, rowId) => [rowId + 1, ...vars.map((variable) => binding[variable] ?? "")]);
|
|
110
114
|
}
|
|
111
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
|
+
|
|
112
144
|
private getUriLinkFromBinding(binding: Parser.BindingValue, prefixes?: { [key: string]: string }) {
|
|
113
145
|
const href = sanitize(binding.value);
|
|
114
146
|
let visibleString = href;
|
|
115
147
|
let prefixed = false;
|
|
116
|
-
if (
|
|
148
|
+
// Apply URI prefixing if enabled (default true)
|
|
149
|
+
if (this.persistentConfig.showUriPrefixes !== false && prefixes) {
|
|
117
150
|
for (const prefixLabel in prefixes) {
|
|
118
151
|
if (visibleString.indexOf(prefixes[prefixLabel]) == 0) {
|
|
119
152
|
visibleString = prefixLabel + ":" + href.substring(prefixes[prefixLabel].length);
|
|
@@ -146,11 +179,14 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
146
179
|
// Return now when in compact mode.
|
|
147
180
|
if (this.persistentConfig.compact) return stringRepresentation;
|
|
148
181
|
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
+
}
|
|
154
190
|
}
|
|
155
191
|
return stringRepresentation;
|
|
156
192
|
}
|
|
@@ -223,15 +259,15 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
223
259
|
this.tableResizer = new ColumnResizer(this.tableEl, {
|
|
224
260
|
widths: this.persistentConfig.compact === true ? widths : [this.getSizeFirstColumn(), ...widths.slice(1)],
|
|
225
261
|
partialRefresh: true,
|
|
226
|
-
onResize: this.persistentConfig.
|
|
262
|
+
onResize: this.persistentConfig.isCompactView !== false && this.setEllipsisHandlers,
|
|
227
263
|
headerOnly: true,
|
|
228
264
|
});
|
|
229
265
|
// DataTables uses the rendered style to decide the widths of columns.
|
|
230
|
-
// Before a draw remove the
|
|
231
|
-
if (this.persistentConfig.
|
|
266
|
+
// Before a draw remove the compactTable styling
|
|
267
|
+
if (this.persistentConfig.isCompactView !== false) {
|
|
232
268
|
this.dataTable?.on("preDraw", () => {
|
|
233
269
|
this.tableResizer?.reset({ disable: true });
|
|
234
|
-
removeClass(this.tableEl, "
|
|
270
|
+
removeClass(this.tableEl, "compactTable");
|
|
235
271
|
this.tableEl?.style.removeProperty("width");
|
|
236
272
|
this.tableEl?.style.setProperty("width", this.tableEl.clientWidth + "px");
|
|
237
273
|
return true; // Indicate it should re-render
|
|
@@ -252,8 +288,8 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
252
288
|
onResize: this.setEllipsisHandlers,
|
|
253
289
|
headerOnly: true,
|
|
254
290
|
});
|
|
255
|
-
// Re-add the
|
|
256
|
-
addClass(this.tableEl, "
|
|
291
|
+
// Re-add the compact styling
|
|
292
|
+
addClass(this.tableEl, "compactTable");
|
|
257
293
|
// Check if cells need the ellipsisHandlers
|
|
258
294
|
this.setEllipsisHandlers();
|
|
259
295
|
});
|
|
@@ -261,8 +297,8 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
261
297
|
|
|
262
298
|
this.drawControls();
|
|
263
299
|
// Draw again but with the events
|
|
264
|
-
if (this.persistentConfig.
|
|
265
|
-
addClass(this.tableEl, "
|
|
300
|
+
if (this.persistentConfig.isCompactView !== false) {
|
|
301
|
+
addClass(this.tableEl, "compactTable");
|
|
266
302
|
this.setEllipsisHandlers();
|
|
267
303
|
}
|
|
268
304
|
// if (this.tableEl.clientWidth > width) this.tableEl.parentElement?.style.setProperty("overflow", "hidden");
|
|
@@ -307,13 +343,53 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
307
343
|
this.draw(this.persistentConfig);
|
|
308
344
|
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
309
345
|
};
|
|
310
|
-
private
|
|
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) => {
|
|
311
361
|
// Store in persistentConfig
|
|
312
|
-
this.persistentConfig.
|
|
362
|
+
this.persistentConfig.showDatatypes = (event.target as HTMLInputElement).checked;
|
|
313
363
|
// Update the table
|
|
314
364
|
this.draw(this.persistentConfig);
|
|
315
365
|
this.yasr.storePluginConfig("table", this.persistentConfig);
|
|
316
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
|
+
};
|
|
317
393
|
/**
|
|
318
394
|
* Draws controls on each update
|
|
319
395
|
*/
|
|
@@ -339,21 +415,53 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
339
415
|
this.tableCompactSwitch.defaultChecked = !!this.persistentConfig.compact;
|
|
340
416
|
this.tableControls.appendChild(toggleWrapper);
|
|
341
417
|
|
|
342
|
-
//
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
addClass(
|
|
348
|
-
|
|
349
|
-
addClass(
|
|
350
|
-
|
|
351
|
-
this.
|
|
352
|
-
|
|
353
|
-
this.
|
|
354
|
-
|
|
355
|
-
this.
|
|
356
|
-
this.tableControls.appendChild(
|
|
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);
|
|
357
465
|
|
|
358
466
|
// Create table filter
|
|
359
467
|
this.tableFilterField = document.createElement("input");
|
|
@@ -363,6 +471,14 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
363
471
|
this.tableControls.appendChild(this.tableFilterField);
|
|
364
472
|
this.tableFilterField.addEventListener("keyup", this.handleTableSearch);
|
|
365
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
|
+
|
|
366
482
|
// Create page wrapper
|
|
367
483
|
const pageSizerWrapper = document.createElement("div");
|
|
368
484
|
pageSizerWrapper.className = "pageSizeWrapper";
|
|
@@ -413,8 +529,12 @@ export default class Table implements Plugin<PluginConfig> {
|
|
|
413
529
|
this.tableSizeField = undefined;
|
|
414
530
|
this.tableCompactSwitch?.removeEventListener("change", this.handleSetCompactToggle);
|
|
415
531
|
this.tableCompactSwitch = undefined;
|
|
416
|
-
this.
|
|
417
|
-
this.
|
|
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;
|
|
418
538
|
// Empty controls
|
|
419
539
|
while (this.tableControls?.firstChild) this.tableControls.firstChild.remove();
|
|
420
540
|
this.tableControls?.remove();
|