@matdata/yasgui 5.2.0 → 5.3.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/README.md +128 -144
- package/build/ts/src/ConfigExportImport.d.ts +16 -0
- package/build/ts/src/ConfigExportImport.js +304 -0
- package/build/ts/src/ConfigExportImport.js.map +1 -0
- package/build/ts/src/PersistentConfig.d.ts +4 -0
- package/build/ts/src/PersistentConfig.js +7 -0
- package/build/ts/src/PersistentConfig.js.map +1 -1
- package/build/ts/src/Tab.d.ts +1 -0
- package/build/ts/src/Tab.js +1 -1
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +4 -0
- package/build/ts/src/TabSettingsModal.js +308 -1
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +152 -106
- package/build/yasgui.min.js.map +4 -4
- package/package.json +1 -1
- package/src/ConfigExportImport.ts +391 -0
- package/src/PersistentConfig.ts +17 -0
- package/src/Tab.ts +3 -2
- package/src/TabSettingsModal.scss +78 -0
- package/src/TabSettingsModal.ts +369 -3
- package/src/sortablejs.d.ts +36 -0
package/src/TabSettingsModal.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { addClass, removeClass } from "@matdata/yasgui-utils";
|
|
2
2
|
import "./TabSettingsModal.scss";
|
|
3
3
|
import Tab from "./Tab";
|
|
4
|
+
import * as ConfigExportImport from "./ConfigExportImport";
|
|
4
5
|
|
|
5
6
|
// Theme toggle icons
|
|
6
7
|
const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -126,14 +127,26 @@ export default class TabSettingsModal {
|
|
|
126
127
|
addClass(prefixTab, "modalTabButton");
|
|
127
128
|
prefixTab.onclick = () => this.switchTab("prefix");
|
|
128
129
|
|
|
130
|
+
const editorTab = document.createElement("button");
|
|
131
|
+
editorTab.textContent = "Editor";
|
|
132
|
+
addClass(editorTab, "modalTabButton");
|
|
133
|
+
editorTab.onclick = () => this.switchTab("editor");
|
|
134
|
+
|
|
129
135
|
const endpointsTab = document.createElement("button");
|
|
130
136
|
endpointsTab.textContent = "Endpoint Buttons";
|
|
131
137
|
addClass(endpointsTab, "modalTabButton");
|
|
132
138
|
endpointsTab.onclick = () => this.switchTab("endpoints");
|
|
133
139
|
|
|
140
|
+
const importExportTab = document.createElement("button");
|
|
141
|
+
importExportTab.textContent = "Import/Export";
|
|
142
|
+
addClass(importExportTab, "modalTabButton");
|
|
143
|
+
importExportTab.onclick = () => this.switchTab("importexport");
|
|
144
|
+
|
|
134
145
|
tabsContainer.appendChild(requestTab);
|
|
135
146
|
tabsContainer.appendChild(prefixTab);
|
|
147
|
+
tabsContainer.appendChild(editorTab);
|
|
136
148
|
tabsContainer.appendChild(endpointsTab);
|
|
149
|
+
tabsContainer.appendChild(importExportTab);
|
|
137
150
|
body.appendChild(tabsContainer);
|
|
138
151
|
|
|
139
152
|
// Tab content containers
|
|
@@ -147,14 +160,26 @@ export default class TabSettingsModal {
|
|
|
147
160
|
prefixContent.id = "prefix-content";
|
|
148
161
|
this.drawPrefixSettings(prefixContent);
|
|
149
162
|
|
|
163
|
+
const editorContent = document.createElement("div");
|
|
164
|
+
addClass(editorContent, "modalTabContent");
|
|
165
|
+
editorContent.id = "editor-content";
|
|
166
|
+
this.drawEditorSettings(editorContent);
|
|
167
|
+
|
|
150
168
|
const endpointsContent = document.createElement("div");
|
|
151
169
|
addClass(endpointsContent, "modalTabContent");
|
|
152
170
|
endpointsContent.id = "endpoints-content";
|
|
153
171
|
this.drawEndpointButtonsSettings(endpointsContent);
|
|
154
172
|
|
|
173
|
+
const importExportContent = document.createElement("div");
|
|
174
|
+
addClass(importExportContent, "modalTabContent");
|
|
175
|
+
importExportContent.id = "importexport-content";
|
|
176
|
+
this.drawImportExportSettings(importExportContent);
|
|
177
|
+
|
|
155
178
|
body.appendChild(requestContent);
|
|
156
179
|
body.appendChild(prefixContent);
|
|
180
|
+
body.appendChild(editorContent);
|
|
157
181
|
body.appendChild(endpointsContent);
|
|
182
|
+
body.appendChild(importExportContent);
|
|
158
183
|
|
|
159
184
|
this.modalContent.appendChild(body);
|
|
160
185
|
|
|
@@ -189,7 +214,9 @@ export default class TabSettingsModal {
|
|
|
189
214
|
if (
|
|
190
215
|
(tabName === "request" && index === 0) ||
|
|
191
216
|
(tabName === "prefix" && index === 1) ||
|
|
192
|
-
(tabName === "
|
|
217
|
+
(tabName === "editor" && index === 2) ||
|
|
218
|
+
(tabName === "endpoints" && index === 3) ||
|
|
219
|
+
(tabName === "importexport" && index === 4)
|
|
193
220
|
) {
|
|
194
221
|
addClass(btn as HTMLElement, "active");
|
|
195
222
|
} else {
|
|
@@ -200,6 +227,11 @@ export default class TabSettingsModal {
|
|
|
200
227
|
contents.forEach((content) => {
|
|
201
228
|
if (content.id === `${tabName}-content`) {
|
|
202
229
|
addClass(content as HTMLElement, "active");
|
|
230
|
+
// Reload editor settings if switching to editor tab and it hasn't been loaded yet
|
|
231
|
+
if (tabName === "editor" && content.innerHTML.indexOf("not yet initialized") > -1) {
|
|
232
|
+
content.innerHTML = "";
|
|
233
|
+
this.drawEditorSettings(content as HTMLElement);
|
|
234
|
+
}
|
|
203
235
|
} else {
|
|
204
236
|
removeClass(content as HTMLElement, "active");
|
|
205
237
|
}
|
|
@@ -246,6 +278,108 @@ export default class TabSettingsModal {
|
|
|
246
278
|
container.appendChild(section);
|
|
247
279
|
}
|
|
248
280
|
|
|
281
|
+
private drawEditorSettings(container: HTMLElement) {
|
|
282
|
+
const yasqe = this.tab.getYasqe();
|
|
283
|
+
if (!yasqe) {
|
|
284
|
+
const notice = document.createElement("div");
|
|
285
|
+
notice.textContent = "Query editor is not yet initialized.";
|
|
286
|
+
addClass(notice, "settingsHelp");
|
|
287
|
+
container.appendChild(notice);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Formatter Type Section
|
|
292
|
+
const formatterSection = document.createElement("div");
|
|
293
|
+
addClass(formatterSection, "settingsSection");
|
|
294
|
+
|
|
295
|
+
const formatterLabel = document.createElement("label");
|
|
296
|
+
formatterLabel.textContent = "Query Formatter";
|
|
297
|
+
addClass(formatterLabel, "settingsLabel");
|
|
298
|
+
|
|
299
|
+
const formatterHelp = document.createElement("div");
|
|
300
|
+
formatterHelp.textContent = "Choose which formatter to use when formatting SPARQL queries (Shift+Ctrl+F).";
|
|
301
|
+
addClass(formatterHelp, "settingsHelp");
|
|
302
|
+
|
|
303
|
+
const formatterSelect = document.createElement("select");
|
|
304
|
+
formatterSelect.id = "formatterTypeSelect";
|
|
305
|
+
addClass(formatterSelect, "settingsSelect");
|
|
306
|
+
|
|
307
|
+
const sparqlFormatterOption = document.createElement("option");
|
|
308
|
+
sparqlFormatterOption.value = "sparql-formatter";
|
|
309
|
+
sparqlFormatterOption.textContent = "SPARQL Formatter (external library)";
|
|
310
|
+
formatterSelect.appendChild(sparqlFormatterOption);
|
|
311
|
+
|
|
312
|
+
const legacyOption = document.createElement("option");
|
|
313
|
+
legacyOption.value = "legacy";
|
|
314
|
+
legacyOption.textContent = "Legacy Formatter (built-in)";
|
|
315
|
+
formatterSelect.appendChild(legacyOption);
|
|
316
|
+
|
|
317
|
+
const currentFormatter = yasqe.persistentConfig?.formatterType || "sparql-formatter";
|
|
318
|
+
formatterSelect.value = currentFormatter;
|
|
319
|
+
|
|
320
|
+
formatterSection.appendChild(formatterLabel);
|
|
321
|
+
formatterSection.appendChild(formatterHelp);
|
|
322
|
+
formatterSection.appendChild(formatterSelect);
|
|
323
|
+
container.appendChild(formatterSection);
|
|
324
|
+
|
|
325
|
+
// Auto-format on Query Section
|
|
326
|
+
const autoformatSection = document.createElement("div");
|
|
327
|
+
addClass(autoformatSection, "settingsSection");
|
|
328
|
+
|
|
329
|
+
const autoformatCheckboxContainer = document.createElement("div");
|
|
330
|
+
addClass(autoformatCheckboxContainer, "checkboxContainer");
|
|
331
|
+
|
|
332
|
+
const autoformatCheckbox = document.createElement("input");
|
|
333
|
+
autoformatCheckbox.type = "checkbox";
|
|
334
|
+
autoformatCheckbox.id = "autoformatOnQuery";
|
|
335
|
+
autoformatCheckbox.checked = yasqe.persistentConfig?.autoformatOnQuery || true;
|
|
336
|
+
|
|
337
|
+
const autoformatLabel = document.createElement("label");
|
|
338
|
+
autoformatLabel.htmlFor = "autoformatOnQuery";
|
|
339
|
+
autoformatLabel.textContent = "Auto-format query before execution";
|
|
340
|
+
|
|
341
|
+
const autoformatHelp = document.createElement("div");
|
|
342
|
+
autoformatHelp.textContent = "Automatically format the query using the selected formatter before executing it.";
|
|
343
|
+
addClass(autoformatHelp, "settingsHelp");
|
|
344
|
+
autoformatHelp.style.marginTop = "5px";
|
|
345
|
+
|
|
346
|
+
autoformatCheckboxContainer.appendChild(autoformatCheckbox);
|
|
347
|
+
autoformatCheckboxContainer.appendChild(autoformatLabel);
|
|
348
|
+
|
|
349
|
+
autoformatSection.appendChild(autoformatCheckboxContainer);
|
|
350
|
+
autoformatSection.appendChild(autoformatHelp);
|
|
351
|
+
container.appendChild(autoformatSection);
|
|
352
|
+
|
|
353
|
+
// CONSTRUCT Variable Validation Section
|
|
354
|
+
const constructValidationSection = document.createElement("div");
|
|
355
|
+
addClass(constructValidationSection, "settingsSection");
|
|
356
|
+
|
|
357
|
+
const constructValidationCheckboxContainer = document.createElement("div");
|
|
358
|
+
addClass(constructValidationCheckboxContainer, "checkboxContainer");
|
|
359
|
+
|
|
360
|
+
const constructValidationCheckbox = document.createElement("input");
|
|
361
|
+
constructValidationCheckbox.type = "checkbox";
|
|
362
|
+
constructValidationCheckbox.id = "checkConstructVariables";
|
|
363
|
+
constructValidationCheckbox.checked = yasqe.config.checkConstructVariables ?? true;
|
|
364
|
+
|
|
365
|
+
const constructValidationLabel = document.createElement("label");
|
|
366
|
+
constructValidationLabel.htmlFor = "checkConstructVariables";
|
|
367
|
+
constructValidationLabel.textContent = "Validate CONSTRUCT query variables";
|
|
368
|
+
|
|
369
|
+
const constructValidationHelp = document.createElement("div");
|
|
370
|
+
constructValidationHelp.textContent =
|
|
371
|
+
"Show warnings for variables used in CONSTRUCT template but not defined in WHERE clause.";
|
|
372
|
+
addClass(constructValidationHelp, "settingsHelp");
|
|
373
|
+
constructValidationHelp.style.marginTop = "5px";
|
|
374
|
+
|
|
375
|
+
constructValidationCheckboxContainer.appendChild(constructValidationCheckbox);
|
|
376
|
+
constructValidationCheckboxContainer.appendChild(constructValidationLabel);
|
|
377
|
+
|
|
378
|
+
constructValidationSection.appendChild(constructValidationCheckboxContainer);
|
|
379
|
+
constructValidationSection.appendChild(constructValidationHelp);
|
|
380
|
+
container.appendChild(constructValidationSection);
|
|
381
|
+
}
|
|
382
|
+
|
|
249
383
|
private drawRequestSettings(container: HTMLElement) {
|
|
250
384
|
// This is a simplified version - you can expand based on TabPanel.ts
|
|
251
385
|
const reqConfig = this.tab.getRequestConfig();
|
|
@@ -312,6 +446,11 @@ export default class TabSettingsModal {
|
|
|
312
446
|
|
|
313
447
|
public open() {
|
|
314
448
|
this.loadSettings();
|
|
449
|
+
// Reload editor settings in case yasqe wasn't available during init
|
|
450
|
+
const editorContent = this.modalContent.querySelector("#editor-content");
|
|
451
|
+
if (editorContent && editorContent.innerHTML === "") {
|
|
452
|
+
this.drawEditorSettings(editorContent as HTMLElement);
|
|
453
|
+
}
|
|
315
454
|
addClass(this.modalOverlay, "open");
|
|
316
455
|
}
|
|
317
456
|
|
|
@@ -344,6 +483,25 @@ export default class TabSettingsModal {
|
|
|
344
483
|
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
345
484
|
this.tab.yasgui.persistentConfig.setAutoCaptureEnabled(autoCapture);
|
|
346
485
|
|
|
486
|
+
// Save editor settings
|
|
487
|
+
const yasqe = this.tab.getYasqe();
|
|
488
|
+
if (yasqe && yasqe.persistentConfig) {
|
|
489
|
+
const formatterSelect = document.getElementById("formatterTypeSelect") as HTMLSelectElement;
|
|
490
|
+
const autoformatCheckbox = document.getElementById("autoformatOnQuery") as HTMLInputElement;
|
|
491
|
+
const constructValidationCheckbox = document.getElementById("checkConstructVariables") as HTMLInputElement;
|
|
492
|
+
|
|
493
|
+
if (formatterSelect) {
|
|
494
|
+
yasqe.persistentConfig.formatterType = formatterSelect.value as "sparql-formatter" | "legacy";
|
|
495
|
+
}
|
|
496
|
+
if (autoformatCheckbox) {
|
|
497
|
+
yasqe.persistentConfig.autoformatOnQuery = autoformatCheckbox.checked;
|
|
498
|
+
}
|
|
499
|
+
if (constructValidationCheckbox) {
|
|
500
|
+
yasqe.setCheckConstructVariables(constructValidationCheckbox.checked);
|
|
501
|
+
}
|
|
502
|
+
yasqe.saveQuery();
|
|
503
|
+
}
|
|
504
|
+
|
|
347
505
|
// Save request settings
|
|
348
506
|
const requestContent = this.modalContent.querySelector("#request-content");
|
|
349
507
|
if (requestContent) {
|
|
@@ -414,10 +572,10 @@ export default class TabSettingsModal {
|
|
|
414
572
|
// If we didn't find a non-PREFIX line, all lines are PREFIX or empty
|
|
415
573
|
if (firstNonPrefixLine === 0 && lines.length > 0) {
|
|
416
574
|
// Check if there's any content at all
|
|
417
|
-
const hasContent = lines.some((line) => line.trim().length > 0);
|
|
575
|
+
const hasContent = lines.some((line: string) => line.trim().length > 0);
|
|
418
576
|
if (
|
|
419
577
|
!hasContent ||
|
|
420
|
-
lines.every((line) => line.trim().length === 0 || line.trim().toUpperCase().startsWith("PREFIX"))
|
|
578
|
+
lines.every((line: string) => line.trim().length === 0 || line.trim().toUpperCase().startsWith("PREFIX"))
|
|
421
579
|
) {
|
|
422
580
|
firstNonPrefixLine = lines.length;
|
|
423
581
|
}
|
|
@@ -574,6 +732,214 @@ export default class TabSettingsModal {
|
|
|
574
732
|
return currentTheme === "dark" ? MOON_ICON : SUN_ICON;
|
|
575
733
|
}
|
|
576
734
|
|
|
735
|
+
private drawImportExportSettings(container: HTMLElement) {
|
|
736
|
+
// Export Section
|
|
737
|
+
const exportSection = document.createElement("div");
|
|
738
|
+
addClass(exportSection, "settingsSection");
|
|
739
|
+
|
|
740
|
+
const exportLabel = document.createElement("label");
|
|
741
|
+
exportLabel.textContent = "Export Configuration";
|
|
742
|
+
addClass(exportLabel, "settingsLabel");
|
|
743
|
+
|
|
744
|
+
const exportHelp = document.createElement("div");
|
|
745
|
+
exportHelp.textContent =
|
|
746
|
+
"Export your YASGUI configuration in RDF Turtle format. This includes tabs, queries, endpoints, and preferences.";
|
|
747
|
+
addClass(exportHelp, "settingsHelp");
|
|
748
|
+
|
|
749
|
+
const exportButtonsContainer = document.createElement("div");
|
|
750
|
+
addClass(exportButtonsContainer, "exportButtons");
|
|
751
|
+
|
|
752
|
+
const copyButton = document.createElement("button");
|
|
753
|
+
copyButton.textContent = "📋 Copy to Clipboard";
|
|
754
|
+
copyButton.type = "button";
|
|
755
|
+
addClass(copyButton, "secondaryButton");
|
|
756
|
+
copyButton.onclick = async () => {
|
|
757
|
+
try {
|
|
758
|
+
const config = this.tab.yasgui.persistentConfig.getPersistedConfig();
|
|
759
|
+
await ConfigExportImport.copyConfigToClipboard(config);
|
|
760
|
+
this.showNotification("Configuration copied to clipboard!", "success");
|
|
761
|
+
} catch (error) {
|
|
762
|
+
this.showNotification("Failed to copy to clipboard: " + (error as Error).message, "error");
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
const downloadButton = document.createElement("button");
|
|
767
|
+
downloadButton.textContent = "💾 Download as File";
|
|
768
|
+
downloadButton.type = "button";
|
|
769
|
+
addClass(downloadButton, "primaryButton");
|
|
770
|
+
downloadButton.onclick = () => {
|
|
771
|
+
try {
|
|
772
|
+
const config = this.tab.yasgui.persistentConfig.getPersistedConfig();
|
|
773
|
+
ConfigExportImport.downloadConfigAsFile(config);
|
|
774
|
+
this.showNotification("Configuration downloaded!", "success");
|
|
775
|
+
} catch (error) {
|
|
776
|
+
this.showNotification("Failed to download: " + (error as Error).message, "error");
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
exportButtonsContainer.appendChild(copyButton);
|
|
781
|
+
exportButtonsContainer.appendChild(downloadButton);
|
|
782
|
+
|
|
783
|
+
exportSection.appendChild(exportLabel);
|
|
784
|
+
exportSection.appendChild(exportHelp);
|
|
785
|
+
exportSection.appendChild(exportButtonsContainer);
|
|
786
|
+
container.appendChild(exportSection);
|
|
787
|
+
|
|
788
|
+
// Import Section
|
|
789
|
+
const importSection = document.createElement("div");
|
|
790
|
+
addClass(importSection, "settingsSection");
|
|
791
|
+
|
|
792
|
+
const importLabel = document.createElement("label");
|
|
793
|
+
importLabel.textContent = "Import Configuration";
|
|
794
|
+
addClass(importLabel, "settingsLabel");
|
|
795
|
+
|
|
796
|
+
const importHelp = document.createElement("div");
|
|
797
|
+
importHelp.textContent = "Import a previously exported configuration in RDF Turtle format.";
|
|
798
|
+
addClass(importHelp, "settingsHelp");
|
|
799
|
+
|
|
800
|
+
// Drag and drop area
|
|
801
|
+
const dropZone = document.createElement("div");
|
|
802
|
+
addClass(dropZone, "dropZone");
|
|
803
|
+
dropZone.innerHTML = `
|
|
804
|
+
<div class="dropZoneContent">
|
|
805
|
+
<div class="dropZoneIcon">📁</div>
|
|
806
|
+
<div class="dropZoneText">Drag & drop a .ttl file here</div>
|
|
807
|
+
<div class="dropZoneOr">or</div>
|
|
808
|
+
</div>
|
|
809
|
+
`;
|
|
810
|
+
|
|
811
|
+
const fileInput = document.createElement("input");
|
|
812
|
+
fileInput.type = "file";
|
|
813
|
+
fileInput.accept = ".ttl,.turtle,text/turtle";
|
|
814
|
+
fileInput.style.display = "none";
|
|
815
|
+
fileInput.id = "config-file-input";
|
|
816
|
+
|
|
817
|
+
const browseButton = document.createElement("button");
|
|
818
|
+
browseButton.textContent = "📂 Browse Files";
|
|
819
|
+
browseButton.type = "button";
|
|
820
|
+
addClass(browseButton, "secondaryButton");
|
|
821
|
+
browseButton.onclick = () => fileInput.click();
|
|
822
|
+
|
|
823
|
+
const pasteButton = document.createElement("button");
|
|
824
|
+
pasteButton.textContent = "📋 Paste from Clipboard";
|
|
825
|
+
pasteButton.type = "button";
|
|
826
|
+
addClass(pasteButton, "secondaryButton");
|
|
827
|
+
pasteButton.onclick = async () => {
|
|
828
|
+
try {
|
|
829
|
+
const content = await ConfigExportImport.readConfigFromClipboard();
|
|
830
|
+
await this.importConfiguration(content);
|
|
831
|
+
} catch (error) {
|
|
832
|
+
this.showNotification("Failed to read from clipboard: " + (error as Error).message, "error");
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
// File input change handler
|
|
837
|
+
fileInput.onchange = async (e) => {
|
|
838
|
+
const file = (e.target as HTMLInputElement).files?.[0];
|
|
839
|
+
if (file) {
|
|
840
|
+
try {
|
|
841
|
+
const content = await ConfigExportImport.readConfigFromFile(file);
|
|
842
|
+
await this.importConfiguration(content);
|
|
843
|
+
} catch (error) {
|
|
844
|
+
this.showNotification("Failed to read file: " + (error as Error).message, "error");
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
// Drag and drop handlers
|
|
850
|
+
dropZone.ondragover = (e) => {
|
|
851
|
+
e.preventDefault();
|
|
852
|
+
addClass(dropZone, "dragover");
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
dropZone.ondragleave = () => {
|
|
856
|
+
removeClass(dropZone, "dragover");
|
|
857
|
+
};
|
|
858
|
+
|
|
859
|
+
dropZone.ondrop = async (e) => {
|
|
860
|
+
e.preventDefault();
|
|
861
|
+
removeClass(dropZone, "dragover");
|
|
862
|
+
|
|
863
|
+
const file = e.dataTransfer?.files?.[0];
|
|
864
|
+
if (file) {
|
|
865
|
+
try {
|
|
866
|
+
const content = await ConfigExportImport.readConfigFromFile(file);
|
|
867
|
+
await this.importConfiguration(content);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
this.showNotification("Failed to read file: " + (error as Error).message, "error");
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
const importButtonsContainer = document.createElement("div");
|
|
875
|
+
addClass(importButtonsContainer, "importButtons");
|
|
876
|
+
importButtonsContainer.appendChild(browseButton);
|
|
877
|
+
importButtonsContainer.appendChild(pasteButton);
|
|
878
|
+
|
|
879
|
+
dropZone.appendChild(importButtonsContainer);
|
|
880
|
+
|
|
881
|
+
importSection.appendChild(importLabel);
|
|
882
|
+
importSection.appendChild(importHelp);
|
|
883
|
+
importSection.appendChild(dropZone);
|
|
884
|
+
importSection.appendChild(fileInput);
|
|
885
|
+
container.appendChild(importSection);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
private async importConfiguration(turtleContent: string) {
|
|
889
|
+
try {
|
|
890
|
+
const parsedConfig = ConfigExportImport.parseFromTurtle(turtleContent);
|
|
891
|
+
|
|
892
|
+
// Confirm with user before importing
|
|
893
|
+
const confirmMsg = `This will replace your current configuration with ${parsedConfig.tabs?.length || 0} tab(s). Continue?`;
|
|
894
|
+
if (!confirm(confirmMsg)) {
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Close the modal first
|
|
899
|
+
this.close();
|
|
900
|
+
|
|
901
|
+
// Apply theme if provided
|
|
902
|
+
if (parsedConfig.theme) {
|
|
903
|
+
this.tab.yasgui.themeManager.setTheme(parsedConfig.theme);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Apply global orientation if provided
|
|
907
|
+
if (parsedConfig.orientation) {
|
|
908
|
+
this.tab.yasgui.config.orientation = parsedConfig.orientation;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Close all existing tabs
|
|
912
|
+
const existingTabIds = [...this.tab.yasgui.persistentConfig.getTabs()];
|
|
913
|
+
for (const tabId of existingTabIds) {
|
|
914
|
+
const tab = this.tab.yasgui.getTab(tabId);
|
|
915
|
+
if (tab) {
|
|
916
|
+
tab.close();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// Update the configuration storage
|
|
921
|
+
this.tab.yasgui.persistentConfig.updatePersistedConfig(parsedConfig);
|
|
922
|
+
|
|
923
|
+
window.location.reload();
|
|
924
|
+
} catch (error) {
|
|
925
|
+
this.showNotification("Failed to import configuration: " + (error as Error).message, "error");
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
private showNotification(message: string, type: "success" | "error") {
|
|
930
|
+
const notification = document.createElement("div");
|
|
931
|
+
addClass(notification, "importExportNotification", type);
|
|
932
|
+
notification.textContent = message;
|
|
933
|
+
|
|
934
|
+
this.modalContent.appendChild(notification);
|
|
935
|
+
|
|
936
|
+
setTimeout(() => {
|
|
937
|
+
if (notification.parentNode) {
|
|
938
|
+
notification.parentNode.removeChild(notification);
|
|
939
|
+
}
|
|
940
|
+
}, 5000);
|
|
941
|
+
}
|
|
942
|
+
|
|
577
943
|
public destroy() {
|
|
578
944
|
if (this.modalOverlay && this.modalOverlay.parentNode) {
|
|
579
945
|
this.modalOverlay.parentNode.removeChild(this.modalOverlay);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
declare module "sortablejs" {
|
|
2
|
+
interface SortableOptions {
|
|
3
|
+
animation?: number;
|
|
4
|
+
handle?: string;
|
|
5
|
+
draggable?: string;
|
|
6
|
+
ghostClass?: string;
|
|
7
|
+
chosenClass?: string;
|
|
8
|
+
dragClass?: string;
|
|
9
|
+
filter?: string;
|
|
10
|
+
preventOnFilter?: boolean;
|
|
11
|
+
onEnd?: (evt: SortableEvent) => void;
|
|
12
|
+
onStart?: (evt: SortableEvent) => void;
|
|
13
|
+
onUpdate?: (evt: SortableEvent) => void;
|
|
14
|
+
onSort?: (evt: SortableEvent) => void;
|
|
15
|
+
onMove?: (evt: SortableEvent, originalEvent: any) => boolean | -1 | 1 | undefined;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface SortableEvent {
|
|
20
|
+
oldIndex?: number;
|
|
21
|
+
newIndex?: number;
|
|
22
|
+
item: HTMLElement;
|
|
23
|
+
to: HTMLElement;
|
|
24
|
+
from: HTMLElement;
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class Sortable {
|
|
29
|
+
constructor(el: HTMLElement, options?: SortableOptions);
|
|
30
|
+
destroy(): void;
|
|
31
|
+
option(name: string, value?: any): any;
|
|
32
|
+
static create(el: HTMLElement, options?: SortableOptions): Sortable;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default Sortable;
|
|
36
|
+
}
|