@matdata/yasgui 5.15.0 → 5.17.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/Tab.d.ts +2 -0
- package/build/ts/src/Tab.js +51 -2
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabContextMenu.d.ts +2 -1
- package/build/ts/src/TabContextMenu.js +11 -3
- package/build/ts/src/TabContextMenu.js.map +1 -1
- package/build/ts/src/TabElements.js +13 -3
- package/build/ts/src/TabElements.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +8 -0
- package/build/ts/src/TabSettingsModal.js +89 -3
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/endpointSelect.js +1 -1
- package/build/ts/src/queryManagement/QueryBrowser.d.ts +1 -0
- package/build/ts/src/queryManagement/QueryBrowser.js +62 -0
- package/build/ts/src/queryManagement/QueryBrowser.js.map +1 -1
- package/build/ts/src/queryManagement/SaveManagedQueryModal.d.ts +6 -0
- package/build/ts/src/queryManagement/SaveManagedQueryModal.js +67 -3
- package/build/ts/src/queryManagement/SaveManagedQueryModal.js.map +1 -1
- package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.d.ts +1 -0
- package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.js +18 -0
- package/build/ts/src/queryManagement/backends/GitWorkspaceBackend.js.map +1 -1
- package/build/ts/src/queryManagement/backends/InMemoryWorkspaceBackend.d.ts +1 -0
- package/build/ts/src/queryManagement/backends/InMemoryWorkspaceBackend.js +17 -0
- package/build/ts/src/queryManagement/backends/InMemoryWorkspaceBackend.js.map +1 -1
- package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.d.ts +1 -0
- package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.js +61 -0
- package/build/ts/src/queryManagement/backends/SparqlWorkspaceBackend.js.map +1 -1
- package/build/ts/src/queryManagement/backends/WorkspaceBackend.d.ts +1 -0
- package/build/ts/src/version.d.ts +1 -1
- package/build/ts/src/version.js +1 -1
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +254 -216
- package/build/yasgui.min.js.map +4 -4
- package/package.json +3 -3
- package/src/Tab.ts +64 -2
- package/src/TabContextMenu.ts +15 -5
- package/src/TabElements.scss +17 -0
- package/src/TabElements.ts +17 -3
- package/src/TabSettingsModal.scss +73 -0
- package/src/TabSettingsModal.ts +117 -3
- package/src/endpointSelect.scss +0 -22
- package/src/endpointSelect.ts +1 -1
- package/src/queryManagement/QueryBrowser.ts +72 -0
- package/src/queryManagement/SaveManagedQueryModal.ts +82 -3
- package/src/queryManagement/backends/GitWorkspaceBackend.ts +19 -0
- package/src/queryManagement/backends/InMemoryWorkspaceBackend.ts +17 -0
- package/src/queryManagement/backends/SparqlWorkspaceBackend.ts +69 -0
- package/src/queryManagement/backends/WorkspaceBackend.ts +6 -0
- package/src/tab.scss +1 -0
- package/src/themes.scss +0 -10
- package/src/version.ts +1 -1
|
@@ -41,6 +41,8 @@ export default class SaveManagedQueryModal {
|
|
|
41
41
|
private saveBtn!: HTMLButtonElement;
|
|
42
42
|
private cancelBtn!: HTMLButtonElement;
|
|
43
43
|
private saveBtnOriginalText = "Save";
|
|
44
|
+
private titleEl!: HTMLHeadingElement;
|
|
45
|
+
private workspaceRowEl!: HTMLDivElement;
|
|
44
46
|
|
|
45
47
|
private filenameTouched = false;
|
|
46
48
|
private folderPickerOpen = false;
|
|
@@ -49,6 +51,8 @@ export default class SaveManagedQueryModal {
|
|
|
49
51
|
|
|
50
52
|
private resolve?: (value: SaveManagedQueryModalResult) => void;
|
|
51
53
|
private reject?: (reason?: unknown) => void;
|
|
54
|
+
private moveResolve?: (value: string | undefined) => void;
|
|
55
|
+
private isMoveMode = false;
|
|
52
56
|
|
|
53
57
|
constructor(yasgui: Yasgui) {
|
|
54
58
|
this.yasgui = yasgui;
|
|
@@ -65,6 +69,7 @@ export default class SaveManagedQueryModal {
|
|
|
65
69
|
|
|
66
70
|
const titleEl = document.createElement("h2");
|
|
67
71
|
titleEl.textContent = "Save as managed query";
|
|
72
|
+
this.titleEl = titleEl;
|
|
68
73
|
|
|
69
74
|
const closeBtn = document.createElement("button");
|
|
70
75
|
closeBtn.type = "button";
|
|
@@ -203,6 +208,7 @@ export default class SaveManagedQueryModal {
|
|
|
203
208
|
this.messageEl.setAttribute("aria-label", "Save message");
|
|
204
209
|
|
|
205
210
|
const workspaceRow = this.row("Workspace", this.workspaceSelectEl);
|
|
211
|
+
this.workspaceRowEl = workspaceRow;
|
|
206
212
|
const folderRow = this.folderRow();
|
|
207
213
|
this.nameRowEl = this.row("Name", this.nameEl);
|
|
208
214
|
this.filenameRowEl = this.row("Filename", this.filenameEl);
|
|
@@ -343,9 +349,24 @@ export default class SaveManagedQueryModal {
|
|
|
343
349
|
this.mouseDownOnOverlay = false;
|
|
344
350
|
this.close();
|
|
345
351
|
this.overlayEl.remove();
|
|
346
|
-
this.
|
|
347
|
-
|
|
348
|
-
|
|
352
|
+
if (this.isMoveMode) {
|
|
353
|
+
this.resetMoveMode();
|
|
354
|
+
const resolve = this.moveResolve;
|
|
355
|
+
this.moveResolve = undefined;
|
|
356
|
+
resolve?.(undefined);
|
|
357
|
+
} else {
|
|
358
|
+
this.reject?.(new Error("cancelled"));
|
|
359
|
+
this.resolve = undefined;
|
|
360
|
+
this.reject = undefined;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private resetMoveMode() {
|
|
365
|
+
this.isMoveMode = false;
|
|
366
|
+
this.titleEl.textContent = "Save as managed query";
|
|
367
|
+
this.saveBtn.textContent = "Save";
|
|
368
|
+
this.saveBtnOriginalText = "Save";
|
|
369
|
+
this.workspaceRowEl.style.display = "";
|
|
349
370
|
}
|
|
350
371
|
|
|
351
372
|
private setLoading(isLoading: boolean) {
|
|
@@ -371,6 +392,18 @@ export default class SaveManagedQueryModal {
|
|
|
371
392
|
}
|
|
372
393
|
|
|
373
394
|
private submit() {
|
|
395
|
+
if (this.isMoveMode) {
|
|
396
|
+
const folderPath = this.folderPathEl.value.trim();
|
|
397
|
+
this.mouseDownOnOverlay = false;
|
|
398
|
+
this.close();
|
|
399
|
+
this.overlayEl.remove();
|
|
400
|
+
this.resetMoveMode();
|
|
401
|
+
const resolve = this.moveResolve;
|
|
402
|
+
this.moveResolve = undefined;
|
|
403
|
+
resolve?.(folderPath);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
374
407
|
const workspaceId = this.workspaceSelectEl.value;
|
|
375
408
|
const name = this.nameEl.value.trim();
|
|
376
409
|
const filename = this.filenameEl.value.trim();
|
|
@@ -570,4 +603,50 @@ export default class SaveManagedQueryModal {
|
|
|
570
603
|
this.newFolderNameEl.value = "";
|
|
571
604
|
void this.refreshFolderPicker();
|
|
572
605
|
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Show a simplified modal containing only the folder picker, for moving an existing query.
|
|
609
|
+
* Resolves with the selected folder path (empty string = root), or `undefined` if cancelled.
|
|
610
|
+
*/
|
|
611
|
+
public async showFolderPickerOnly(workspaceId: string, currentFolderPath: string): Promise<string | undefined> {
|
|
612
|
+
const workspaces = this.yasgui.persistentConfig.getWorkspaces();
|
|
613
|
+
|
|
614
|
+
this.workspaceSelectEl.innerHTML = "";
|
|
615
|
+
for (const w of workspaces) {
|
|
616
|
+
const opt = document.createElement("option");
|
|
617
|
+
opt.value = w.id;
|
|
618
|
+
opt.textContent = w.label;
|
|
619
|
+
this.workspaceSelectEl.appendChild(opt);
|
|
620
|
+
}
|
|
621
|
+
this.workspaceSelectEl.value = workspaceId;
|
|
622
|
+
|
|
623
|
+
// Hide rows not needed for a move operation.
|
|
624
|
+
this.workspaceRowEl.style.display = "none";
|
|
625
|
+
this.nameRowEl.style.display = "none";
|
|
626
|
+
this.filenameRowEl.style.display = "none";
|
|
627
|
+
this.messageRowEl.style.display = "none";
|
|
628
|
+
|
|
629
|
+
this.folderPathEl.value = currentFolderPath;
|
|
630
|
+
this.filenameTouched = false;
|
|
631
|
+
this.folderPickerOpen = false;
|
|
632
|
+
removeClass(this.folderPickerEl, "open");
|
|
633
|
+
this.folderBrowsePath = currentFolderPath;
|
|
634
|
+
this.folderPickerErrorEl.textContent = "";
|
|
635
|
+
this.folderPickerListEl.innerHTML = "";
|
|
636
|
+
this.mouseDownOnOverlay = false;
|
|
637
|
+
|
|
638
|
+
this.titleEl.textContent = "Move to folder";
|
|
639
|
+
this.saveBtn.textContent = "Move";
|
|
640
|
+
this.saveBtnOriginalText = "Move";
|
|
641
|
+
|
|
642
|
+
this.isMoveMode = true;
|
|
643
|
+
|
|
644
|
+
document.body.appendChild(this.overlayEl);
|
|
645
|
+
this.open();
|
|
646
|
+
this.folderPickerToggleEl.focus();
|
|
647
|
+
|
|
648
|
+
return new Promise<string | undefined>((resolve) => {
|
|
649
|
+
this.moveResolve = resolve;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
573
652
|
}
|
|
@@ -69,6 +69,25 @@ export default class GitWorkspaceBackend implements WorkspaceBackend {
|
|
|
69
69
|
return this.client.deleteQuery(this.config, _queryId);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
async moveQuery(queryId: string, newFolderId: string): Promise<string> {
|
|
73
|
+
if (!this.client) throw this.missingClientError();
|
|
74
|
+
|
|
75
|
+
const parts = queryId.split("/").filter(Boolean);
|
|
76
|
+
const filename = parts[parts.length - 1] || queryId;
|
|
77
|
+
const folder = newFolderId.replace(/^\/+|\/+$/g, "");
|
|
78
|
+
|
|
79
|
+
const newPath = folder ? `${folder}/${filename}` : filename;
|
|
80
|
+
if (newPath === queryId) return queryId;
|
|
81
|
+
|
|
82
|
+
const read = await this.client.readQuery(this.config, queryId);
|
|
83
|
+
await this.client.writeQuery(this.config, newPath, read.queryText, {
|
|
84
|
+
message: `Move ${filename} to ${folder || "(root)"}`,
|
|
85
|
+
});
|
|
86
|
+
await this.client.deleteQuery(this.config, queryId);
|
|
87
|
+
|
|
88
|
+
return newPath;
|
|
89
|
+
}
|
|
90
|
+
|
|
72
91
|
async renameQuery(queryId: string, newLabel: string): Promise<void> {
|
|
73
92
|
if (!this.client) throw this.missingClientError();
|
|
74
93
|
|
|
@@ -152,6 +152,23 @@ export default class InMemoryWorkspaceBackend implements WorkspaceBackend {
|
|
|
152
152
|
return { queryText: found.queryText, versionTag: found.id };
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
async moveQuery(queryId: string, newFolderId: string): Promise<string> {
|
|
156
|
+
const versions = this.versionsByQueryId.get(queryId);
|
|
157
|
+
if (!versions || versions.length === 0) throw new WorkspaceBackendError("NOT_FOUND", "Query not found");
|
|
158
|
+
|
|
159
|
+
const filename = basename(queryId);
|
|
160
|
+
const folder = normalizeFolderId(newFolderId);
|
|
161
|
+
const newId = folder ? `${folder}/${filename}` : filename;
|
|
162
|
+
if (newId === queryId) return queryId;
|
|
163
|
+
|
|
164
|
+
if (this.versionsByQueryId.has(newId))
|
|
165
|
+
throw new WorkspaceBackendError("CONFLICT", "A query already exists at this path");
|
|
166
|
+
|
|
167
|
+
this.versionsByQueryId.delete(queryId);
|
|
168
|
+
this.versionsByQueryId.set(newId, versions);
|
|
169
|
+
return newId;
|
|
170
|
+
}
|
|
171
|
+
|
|
155
172
|
async renameQuery(queryId: string, newLabel: string): Promise<void> {
|
|
156
173
|
const trimmed = newLabel.trim();
|
|
157
174
|
if (!trimmed) throw new WorkspaceBackendError("UNKNOWN", "New name is required");
|
|
@@ -422,6 +422,75 @@ WHERE { OPTIONAL { ${iri(mqIri)} rdfs:label ?oldLabel . } }
|
|
|
422
422
|
await this.sparqlUpdate(update);
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
async moveQuery(queryId: string, newFolderId: string): Promise<string> {
|
|
426
|
+
const mqIriValue = this.resolveManagedQueryIri(queryId);
|
|
427
|
+
const workspaceIri = this.config.workspaceIri;
|
|
428
|
+
const folder = (newFolderId || "").replace(/^\/+|\/+$/g, "");
|
|
429
|
+
|
|
430
|
+
const newContainerIriValue = folder ? mintFolderIri(workspaceIri, folder) : workspaceIri;
|
|
431
|
+
|
|
432
|
+
// Fetch the current container to detect no-ops.
|
|
433
|
+
const currentContainerIriValue = await (async () => {
|
|
434
|
+
const q = `
|
|
435
|
+
PREFIX yasgui: <https://matdata.eu/ns/yasgui#>
|
|
436
|
+
PREFIX dcterms: <http://purl.org/dc/terms/>
|
|
437
|
+
|
|
438
|
+
SELECT ?container WHERE {
|
|
439
|
+
${iri(mqIriValue)} a yasgui:ManagedQuery ;
|
|
440
|
+
dcterms:isPartOf ?container .
|
|
441
|
+
}
|
|
442
|
+
LIMIT 1`;
|
|
443
|
+
const res = await this.sparqlQuery<SparqlJsonResults>(q);
|
|
444
|
+
const row = this.getBindings(res)[0];
|
|
445
|
+
const container = row?.container?.value;
|
|
446
|
+
if (!container) throw new WorkspaceBackendError("NOT_FOUND", "Query not found");
|
|
447
|
+
return container;
|
|
448
|
+
})();
|
|
449
|
+
|
|
450
|
+
if (currentContainerIriValue === newContainerIriValue) return queryId;
|
|
451
|
+
|
|
452
|
+
// Ensure each ancestor folder exists in the store.
|
|
453
|
+
if (folder) {
|
|
454
|
+
const parts = splitPath(folder);
|
|
455
|
+
const folderTriples: string[] = [];
|
|
456
|
+
for (let i = 0; i < parts.length; i++) {
|
|
457
|
+
const subPath = parts.slice(0, i + 1).join("/");
|
|
458
|
+
const folderIriValue = mintFolderIri(workspaceIri, subPath);
|
|
459
|
+
const folderLabel = parts[i];
|
|
460
|
+
folderTriples.push(`${iri(folderIriValue)} a <https://matdata.eu/ns/yasgui#WorkspaceFolder> ;`);
|
|
461
|
+
folderTriples.push(` <http://www.w3.org/2004/02/skos/core#inScheme> ${iri(workspaceIri)} ;`);
|
|
462
|
+
folderTriples.push(` <http://www.w3.org/2000/01/rdf-schema#label> ${sparqlStringLiteral(folderLabel)} .`);
|
|
463
|
+
|
|
464
|
+
if (i > 0) {
|
|
465
|
+
const parentPath = parts.slice(0, i).join("/");
|
|
466
|
+
const parentIri = mintFolderIri(workspaceIri, parentPath);
|
|
467
|
+
folderTriples.push(
|
|
468
|
+
`${iri(folderIriValue)} <http://www.w3.org/2004/02/skos/core#broader> ${iri(parentIri)} .`,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
await this.sparqlUpdate(`
|
|
473
|
+
PREFIX yasgui: <https://matdata.eu/ns/yasgui#>
|
|
474
|
+
|
|
475
|
+
INSERT DATA {
|
|
476
|
+
${iri(workspaceIri)} a yasgui:Workspace .
|
|
477
|
+
${folderTriples.join("\n ")}
|
|
478
|
+
}
|
|
479
|
+
`);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Update dcterms:isPartOf to point to the new container.
|
|
483
|
+
await this.sparqlUpdate(`
|
|
484
|
+
PREFIX dcterms: <http://purl.org/dc/terms/>
|
|
485
|
+
|
|
486
|
+
DELETE { ${iri(mqIriValue)} dcterms:isPartOf ${iri(currentContainerIriValue)} . }
|
|
487
|
+
INSERT { ${iri(mqIriValue)} dcterms:isPartOf ${iri(newContainerIriValue)} . }
|
|
488
|
+
WHERE { ${iri(mqIriValue)} dcterms:isPartOf ${iri(currentContainerIriValue)} . }
|
|
489
|
+
`);
|
|
490
|
+
|
|
491
|
+
return queryId;
|
|
492
|
+
}
|
|
493
|
+
|
|
425
494
|
async deleteQuery(queryId: string): Promise<void> {
|
|
426
495
|
const mqIri = this.resolveManagedQueryIri(queryId);
|
|
427
496
|
const update = `
|
|
@@ -23,6 +23,12 @@ export interface WorkspaceBackend {
|
|
|
23
23
|
*/
|
|
24
24
|
renameQuery?(queryId: string, newLabel: string): Promise<void>;
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Optional: Move a query to a different folder.
|
|
28
|
+
* Returns the new query ID (may differ from the original for Git backends where the ID encodes the path).
|
|
29
|
+
*/
|
|
30
|
+
moveQuery?(queryId: string, newFolderId: string): Promise<string>;
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Optional: Delete a query and its version history.
|
|
28
34
|
* Implementations may not support this (e.g., some Git provider clients).
|
package/src/tab.scss
CHANGED
package/src/themes.scss
CHANGED
|
@@ -232,16 +232,6 @@
|
|
|
232
232
|
color: var(--yasgui-text-muted);
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
-
|
|
236
|
-
.clearEndpointBtn {
|
|
237
|
-
background-color: var(--yasgui-bg-secondary) !important;
|
|
238
|
-
border-color: var(--yasgui-border-color) !important;
|
|
239
|
-
color: var(--yasgui-button-text) !important;
|
|
240
|
-
|
|
241
|
-
&:hover {
|
|
242
|
-
background-color: var(--yasgui-bg-tertiary) !important;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
235
|
}
|
|
246
236
|
|
|
247
237
|
// Yasqe (editor) theming
|
package/src/version.ts
CHANGED