sqlite-hub 0.5.0 → 0.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/README.md +109 -2
- package/bin/sqlite-hub.js +285 -4
- package/changelog.md +15 -0
- package/frontend/assets/mockups/connections.png +0 -0
- package/frontend/assets/mockups/data.png +0 -0
- package/frontend/assets/mockups/data_row_editor.png +0 -0
- package/frontend/assets/mockups/home.png +0 -0
- package/frontend/assets/mockups/sql_editor.png +0 -0
- package/frontend/assets/mockups/sql_editor_croped.png +0 -0
- package/frontend/assets/mockups/sql_editor_querydetail.png +0 -0
- package/frontend/assets/mockups/structure.png +0 -0
- package/frontend/assets/mockups/structure_inspector.png +0 -0
- package/frontend/index.html +1 -0
- package/frontend/js/api.js +49 -0
- package/frontend/js/app.js +377 -9
- package/frontend/js/components/modal.js +314 -1
- package/frontend/js/components/queryChartRenderer.js +125 -0
- package/frontend/js/components/queryEditor.js +37 -8
- package/frontend/js/components/queryHistoryPanel.js +16 -5
- package/frontend/js/components/sidebar.js +2 -0
- package/frontend/js/components/tableDesignerEditor.js +356 -0
- package/frontend/js/components/tableDesignerSidebar.js +129 -0
- package/frontend/js/components/tableDesignerSqlPreview.js +40 -0
- package/frontend/js/lib/queryChartOptions.js +283 -0
- package/frontend/js/lib/queryCharts.js +560 -0
- package/frontend/js/router.js +18 -0
- package/frontend/js/store.js +914 -1
- package/frontend/js/utils/tableDesigner.js +1192 -0
- package/frontend/js/views/charts.js +471 -0
- package/frontend/js/views/data.js +281 -277
- package/frontend/js/views/editor.js +19 -13
- package/frontend/js/views/structure.js +81 -39
- package/frontend/js/views/tableDesigner.js +37 -0
- package/frontend/styles/base.css +87 -73
- package/frontend/styles/components.css +790 -251
- package/frontend/styles/views.css +316 -46
- package/package.json +2 -1
- package/server/routes/charts.js +153 -0
- package/server/routes/tableDesigner.js +60 -0
- package/server/server.js +10 -0
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +295 -0
- package/server/services/sqlite/tableDesigner/schemaMapping.js +233 -0
- package/server/services/sqlite/tableDesigner/sql.js +63 -0
- package/server/services/sqlite/tableDesigner/validation.js +245 -0
- package/server/services/sqlite/tableDesignerService.js +181 -0
- package/server/services/storage/appStateStore.js +400 -1
- package/server/services/storage/queryHistoryChartUtils.js +145 -0
- package/frontend/assets/mockups/data_edit.png +0 -0
- package/frontend/assets/mockups/graph_visualize.png +0 -0
- package/frontend/assets/mockups/overview.png +0 -0
package/frontend/js/app.js
CHANGED
|
@@ -4,9 +4,14 @@ import { renderSidebar } from "./components/sidebar.js";
|
|
|
4
4
|
import { renderStatusBar } from "./components/statusBar.js";
|
|
5
5
|
import {
|
|
6
6
|
mountStructureGraph,
|
|
7
|
-
resetPersistedStructureGraphState,
|
|
8
7
|
teardownStructureGraph,
|
|
8
|
+
resetPersistedStructureGraphState,
|
|
9
9
|
} from "./components/structureGraph.js";
|
|
10
|
+
import {
|
|
11
|
+
exportQueryChartAsPng,
|
|
12
|
+
mountQueryChartRenderer,
|
|
13
|
+
teardownQueryChartRenderer,
|
|
14
|
+
} from "./components/queryChartRenderer.js";
|
|
10
15
|
import { renderToasts } from "./components/toast.js";
|
|
11
16
|
import { renderTopNav } from "./components/topNav.js";
|
|
12
17
|
import { createRouter } from "./router.js";
|
|
@@ -31,21 +36,31 @@ import {
|
|
|
31
36
|
openQueryHistoryInEditor,
|
|
32
37
|
openDeleteDataRowModal,
|
|
33
38
|
openDeleteEditorRowModal,
|
|
39
|
+
openDeleteQueryChartModal,
|
|
34
40
|
openEditConnectionModal,
|
|
41
|
+
openCreateQueryChartModal,
|
|
42
|
+
openEditQueryChartModal,
|
|
35
43
|
refreshCurrentRoute,
|
|
36
44
|
removeConnection,
|
|
37
45
|
runQueryHistoryItem,
|
|
46
|
+
saveCurrentQueryChartDraft,
|
|
38
47
|
selectDataRow,
|
|
39
48
|
selectEditorRow,
|
|
40
49
|
selectConnection,
|
|
41
50
|
selectQueryHistoryItem,
|
|
42
51
|
selectStructureEntry,
|
|
52
|
+
setTableDesignerSearchQuery,
|
|
43
53
|
setDataPage,
|
|
44
54
|
setDataPageSize,
|
|
45
55
|
setDataSearchColumn,
|
|
46
56
|
setDataSearchQuery,
|
|
57
|
+
toggleDataTablesPanel,
|
|
47
58
|
setCurrentQuery,
|
|
59
|
+
setChartsHeightPreset,
|
|
60
|
+
setEditorPanelVisibility,
|
|
48
61
|
setEditorTab,
|
|
62
|
+
submitDeleteChartConfirmation,
|
|
63
|
+
setQueryHistoryPanelVisibility,
|
|
49
64
|
sortDataTableByColumn,
|
|
50
65
|
sortEditorResultsByColumn,
|
|
51
66
|
setQueryHistorySearchInput,
|
|
@@ -53,6 +68,11 @@ import {
|
|
|
53
68
|
setRoute,
|
|
54
69
|
saveQueryHistoryNotes,
|
|
55
70
|
saveQueryHistoryTitle,
|
|
71
|
+
saveCurrentTableDesignerDraft,
|
|
72
|
+
toggleChartsResultsPanel,
|
|
73
|
+
toggleChartsSqlPanel,
|
|
74
|
+
queueTableDesignerCsvImport,
|
|
75
|
+
showToast,
|
|
56
76
|
submitCreateConnection,
|
|
57
77
|
submitDeleteRowConfirmation,
|
|
58
78
|
submitDataRowUpdate,
|
|
@@ -62,7 +82,14 @@ import {
|
|
|
62
82
|
submitOpenConnection,
|
|
63
83
|
subscribe,
|
|
64
84
|
toggleQueryHistorySavedState,
|
|
85
|
+
updateCurrentQueryChartDraftConfigField,
|
|
86
|
+
updateCurrentQueryChartDraftField,
|
|
87
|
+
updateCurrentTableDesignerColumnField,
|
|
88
|
+
updateCurrentTableDesignerField,
|
|
89
|
+
addCurrentTableDesignerColumn,
|
|
90
|
+
removeCurrentTableDesignerColumn,
|
|
65
91
|
} from "./store.js";
|
|
92
|
+
import { renderChartsView } from "./views/charts.js";
|
|
66
93
|
import { renderConnectionsView } from "./views/connections.js";
|
|
67
94
|
import { renderDataView } from "./views/data.js";
|
|
68
95
|
import { renderEditorView } from "./views/editor.js";
|
|
@@ -70,6 +97,7 @@ import { renderLandingView } from "./views/landing.js";
|
|
|
70
97
|
import { renderOverviewView } from "./views/overview.js";
|
|
71
98
|
import { renderSettingsView } from "./views/settings.js";
|
|
72
99
|
import { renderStructureView } from "./views/structure.js";
|
|
100
|
+
import { renderTableDesignerView } from "./views/tableDesigner.js";
|
|
73
101
|
import { highlightSql } from "./utils/format.js";
|
|
74
102
|
|
|
75
103
|
const appRoot = document.querySelector("#app");
|
|
@@ -86,6 +114,8 @@ const shellRefs = {
|
|
|
86
114
|
modal: document.querySelector("#modal-root"),
|
|
87
115
|
toast: document.querySelector("#toast-root"),
|
|
88
116
|
};
|
|
117
|
+
let lastRenderedRoutePath = null;
|
|
118
|
+
let pendingNewTableDesignerAutofocus = false;
|
|
89
119
|
|
|
90
120
|
function resetStructureGraphForDatabaseChange() {
|
|
91
121
|
resetPersistedStructureGraphState();
|
|
@@ -146,6 +176,21 @@ function readFileAsBase64(file) {
|
|
|
146
176
|
});
|
|
147
177
|
}
|
|
148
178
|
|
|
179
|
+
function readFileAsText(file) {
|
|
180
|
+
return new Promise((resolve, reject) => {
|
|
181
|
+
const reader = new FileReader();
|
|
182
|
+
|
|
183
|
+
reader.onload = () => {
|
|
184
|
+
resolve(String(reader.result ?? ""));
|
|
185
|
+
};
|
|
186
|
+
reader.onerror = () => {
|
|
187
|
+
reject(reader.error ?? new Error("The selected CSV file could not be read."));
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
reader.readAsText(file);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
149
194
|
async function buildConnectionLogoUpload(file) {
|
|
150
195
|
if (!(file instanceof File) || !file.size) {
|
|
151
196
|
return null;
|
|
@@ -192,6 +237,8 @@ function resolveView(state) {
|
|
|
192
237
|
return renderConnectionsView(state);
|
|
193
238
|
case "overview":
|
|
194
239
|
return renderOverviewView(state);
|
|
240
|
+
case "charts":
|
|
241
|
+
return renderChartsView(state);
|
|
195
242
|
case "data":
|
|
196
243
|
return renderDataView(state);
|
|
197
244
|
case "editor":
|
|
@@ -200,6 +247,8 @@ function resolveView(state) {
|
|
|
200
247
|
return renderEditorView(state, { isResultsRoute: true });
|
|
201
248
|
case "structure":
|
|
202
249
|
return renderStructureView(state);
|
|
250
|
+
case "tableDesigner":
|
|
251
|
+
return renderTableDesignerView(state);
|
|
203
252
|
case "settings":
|
|
204
253
|
return renderSettingsView(state);
|
|
205
254
|
default:
|
|
@@ -212,7 +261,11 @@ function captureFocusedInputState() {
|
|
|
212
261
|
|
|
213
262
|
if (
|
|
214
263
|
!activeElement ||
|
|
215
|
-
!(
|
|
264
|
+
!(
|
|
265
|
+
activeElement instanceof HTMLInputElement ||
|
|
266
|
+
activeElement instanceof HTMLTextAreaElement ||
|
|
267
|
+
activeElement instanceof HTMLSelectElement
|
|
268
|
+
)
|
|
216
269
|
) {
|
|
217
270
|
return null;
|
|
218
271
|
}
|
|
@@ -224,6 +277,8 @@ function captureFocusedInputState() {
|
|
|
224
277
|
|
|
225
278
|
return {
|
|
226
279
|
bind,
|
|
280
|
+
field: activeElement.dataset.field ?? null,
|
|
281
|
+
columnId: activeElement.dataset.columnId ?? null,
|
|
227
282
|
selectionStart: activeElement.selectionStart,
|
|
228
283
|
selectionEnd: activeElement.selectionEnd,
|
|
229
284
|
selectionDirection: activeElement.selectionDirection,
|
|
@@ -232,24 +287,61 @@ function captureFocusedInputState() {
|
|
|
232
287
|
};
|
|
233
288
|
}
|
|
234
289
|
|
|
290
|
+
function buildFocusedInputSelectors(snapshot) {
|
|
291
|
+
if (!snapshot?.bind) {
|
|
292
|
+
return [];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const selectors = [];
|
|
296
|
+
const bindSelector = `[data-bind="${CSS.escape(snapshot.bind)}"]`;
|
|
297
|
+
|
|
298
|
+
if (snapshot.columnId && snapshot.field) {
|
|
299
|
+
selectors.push(
|
|
300
|
+
`${bindSelector}[data-column-id="${CSS.escape(snapshot.columnId)}"][data-field="${CSS.escape(snapshot.field)}"]`
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (snapshot.field) {
|
|
305
|
+
selectors.push(`${bindSelector}[data-field="${CSS.escape(snapshot.field)}"]`);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (snapshot.columnId) {
|
|
309
|
+
selectors.push(`${bindSelector}[data-column-id="${CSS.escape(snapshot.columnId)}"]`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
selectors.push(bindSelector);
|
|
313
|
+
return selectors;
|
|
314
|
+
}
|
|
315
|
+
|
|
235
316
|
function restoreFocusedInputState(snapshot) {
|
|
236
317
|
if (!snapshot) {
|
|
237
|
-
return;
|
|
318
|
+
return false;
|
|
238
319
|
}
|
|
239
320
|
|
|
240
|
-
const
|
|
241
|
-
|
|
321
|
+
const nextElement = buildFocusedInputSelectors(snapshot)
|
|
322
|
+
.map((selector) => document.querySelector(selector))
|
|
323
|
+
.find(
|
|
324
|
+
(candidate) =>
|
|
325
|
+
candidate instanceof HTMLInputElement ||
|
|
326
|
+
candidate instanceof HTMLTextAreaElement ||
|
|
327
|
+
candidate instanceof HTMLSelectElement
|
|
328
|
+
);
|
|
242
329
|
|
|
243
330
|
if (
|
|
244
331
|
!nextElement ||
|
|
245
|
-
!(
|
|
332
|
+
!(
|
|
333
|
+
nextElement instanceof HTMLInputElement ||
|
|
334
|
+
nextElement instanceof HTMLTextAreaElement ||
|
|
335
|
+
nextElement instanceof HTMLSelectElement
|
|
336
|
+
)
|
|
246
337
|
) {
|
|
247
|
-
return;
|
|
338
|
+
return false;
|
|
248
339
|
}
|
|
249
340
|
|
|
250
341
|
nextElement.focus({ preventScroll: true });
|
|
251
342
|
|
|
252
343
|
if (
|
|
344
|
+
(nextElement instanceof HTMLInputElement || nextElement instanceof HTMLTextAreaElement) &&
|
|
253
345
|
typeof snapshot.selectionStart === "number" &&
|
|
254
346
|
typeof snapshot.selectionEnd === "number"
|
|
255
347
|
) {
|
|
@@ -262,16 +354,100 @@ function restoreFocusedInputState(snapshot) {
|
|
|
262
354
|
|
|
263
355
|
nextElement.scrollTop = snapshot.scrollTop;
|
|
264
356
|
nextElement.scrollLeft = snapshot.scrollLeft;
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function focusNewTableDesignerNameField() {
|
|
361
|
+
const input = document.querySelector(
|
|
362
|
+
'[data-bind="table-designer-field"][data-field="tableName"]'
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
input.focus({ preventScroll: true });
|
|
370
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function focusTableDesignerColumnNameField(columnId) {
|
|
375
|
+
if (!columnId) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const input = document.querySelector(
|
|
380
|
+
`[data-bind="table-designer-column-field"][data-column-id="${CSS.escape(columnId)}"][data-field="name"]`
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
if (!(input instanceof HTMLInputElement)) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
input.focus({ preventScroll: true });
|
|
388
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function handleTableDesignerCsvImport(fileInput) {
|
|
393
|
+
if (!(fileInput instanceof HTMLInputElement)) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const file = fileInput.files?.[0];
|
|
398
|
+
|
|
399
|
+
if (!(file instanceof File)) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (!file.size) {
|
|
404
|
+
showToast("The selected CSV file is empty.", "alert");
|
|
405
|
+
fileInput.value = "";
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
const csvText = await readFileAsText(file);
|
|
411
|
+
const imported = queueTableDesignerCsvImport(file.name, csvText);
|
|
412
|
+
|
|
413
|
+
if (!imported) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
router.navigate("/table-designer/new");
|
|
418
|
+
showToast(
|
|
419
|
+
`Imported ${imported.columnCount} columns and ${imported.importedRowCount} row${
|
|
420
|
+
imported.importedRowCount === 1 ? "" : "s"
|
|
421
|
+
} from ${file.name}.`,
|
|
422
|
+
"success"
|
|
423
|
+
);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
showToast(error?.message || "CSV import failed.", "alert");
|
|
426
|
+
} finally {
|
|
427
|
+
fileInput.value = "";
|
|
428
|
+
}
|
|
265
429
|
}
|
|
266
430
|
|
|
267
431
|
function renderApp(state) {
|
|
268
432
|
const focusedInput = captureFocusedInputState();
|
|
433
|
+
const previousRoutePath = lastRenderedRoutePath;
|
|
269
434
|
const { main, panel } = resolveView(state);
|
|
270
|
-
const isLockedRoute = ["editor", "editorResults", "data", "structure"].includes(
|
|
435
|
+
const isLockedRoute = ["editor", "editorResults", "data", "charts", "structure", "tableDesigner"].includes(
|
|
271
436
|
state.route.name
|
|
272
437
|
);
|
|
438
|
+
const isEnteringNewTableDesignerRoute =
|
|
439
|
+
state.route.name === "tableDesigner" &&
|
|
440
|
+
state.route.params?.isNew &&
|
|
441
|
+
previousRoutePath !== state.route.path;
|
|
442
|
+
|
|
443
|
+
if (isEnteringNewTableDesignerRoute) {
|
|
444
|
+
pendingNewTableDesignerAutofocus = true;
|
|
445
|
+
} else if (state.route.name !== "tableDesigner" || !state.route.params?.isNew) {
|
|
446
|
+
pendingNewTableDesignerAutofocus = false;
|
|
447
|
+
}
|
|
273
448
|
|
|
274
449
|
teardownStructureGraph();
|
|
450
|
+
teardownQueryChartRenderer();
|
|
275
451
|
shellRefs.topNav.innerHTML = renderTopNav(state);
|
|
276
452
|
shellRefs.sidebar.innerHTML = renderSidebar(state);
|
|
277
453
|
shellRefs.statusBar.innerHTML = renderStatusBar(state);
|
|
@@ -281,13 +457,31 @@ function renderApp(state) {
|
|
|
281
457
|
shellRefs.modal.innerHTML = renderModal(state);
|
|
282
458
|
shellRefs.toast.innerHTML = renderToasts(state.toasts);
|
|
283
459
|
shellRefs.shell.classList.toggle("panel-open", Boolean(panel));
|
|
284
|
-
|
|
460
|
+
|
|
461
|
+
if (
|
|
462
|
+
pendingNewTableDesignerAutofocus &&
|
|
463
|
+
state.route.name === "tableDesigner" &&
|
|
464
|
+
state.route.params?.isNew &&
|
|
465
|
+
state.tableDesigner.draft?.mode === "create"
|
|
466
|
+
) {
|
|
467
|
+
if (focusNewTableDesignerNameField()) {
|
|
468
|
+
pendingNewTableDesignerAutofocus = false;
|
|
469
|
+
}
|
|
470
|
+
} else {
|
|
471
|
+
restoreFocusedInputState(focusedInput);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
lastRenderedRoutePath = state.route.path;
|
|
285
475
|
|
|
286
476
|
if (state.route.name === "structure") {
|
|
287
477
|
mountStructureGraph(state).catch((error) => {
|
|
288
478
|
console.error("Failed to mount structure graph.", error);
|
|
289
479
|
});
|
|
290
480
|
}
|
|
481
|
+
|
|
482
|
+
if (state.route.name === "charts") {
|
|
483
|
+
mountQueryChartRenderer(state);
|
|
484
|
+
}
|
|
291
485
|
}
|
|
292
486
|
|
|
293
487
|
const router = createRouter((route) => {
|
|
@@ -312,6 +506,15 @@ async function handleAction(actionNode) {
|
|
|
312
506
|
case "open-modal":
|
|
313
507
|
openModal(actionNode.dataset.modal);
|
|
314
508
|
return;
|
|
509
|
+
case "open-create-query-chart-modal":
|
|
510
|
+
openCreateQueryChartModal();
|
|
511
|
+
return;
|
|
512
|
+
case "open-edit-query-chart-modal":
|
|
513
|
+
openEditQueryChartModal(actionNode.dataset.chartId);
|
|
514
|
+
return;
|
|
515
|
+
case "open-delete-query-chart-modal":
|
|
516
|
+
openDeleteQueryChartModal(actionNode.dataset.chartId);
|
|
517
|
+
return;
|
|
315
518
|
case "edit-connection":
|
|
316
519
|
openEditConnectionModal(actionNode.dataset.connectionId);
|
|
317
520
|
return;
|
|
@@ -386,6 +589,16 @@ async function handleAction(actionNode) {
|
|
|
386
589
|
await setQueryHistoryTab(actionNode.dataset.tab);
|
|
387
590
|
}
|
|
388
591
|
return;
|
|
592
|
+
case "toggle-query-history-panel":
|
|
593
|
+
setQueryHistoryPanelVisibility(
|
|
594
|
+
actionNode.dataset.nextValue ? actionNode.dataset.nextValue === "true" : undefined
|
|
595
|
+
);
|
|
596
|
+
return;
|
|
597
|
+
case "toggle-editor-panel":
|
|
598
|
+
setEditorPanelVisibility(
|
|
599
|
+
actionNode.dataset.nextValue ? actionNode.dataset.nextValue === "true" : undefined
|
|
600
|
+
);
|
|
601
|
+
return;
|
|
389
602
|
case "load-more-query-history":
|
|
390
603
|
await loadMoreQueryHistory();
|
|
391
604
|
return;
|
|
@@ -408,6 +621,22 @@ async function handleAction(actionNode) {
|
|
|
408
621
|
);
|
|
409
622
|
}
|
|
410
623
|
return;
|
|
624
|
+
case "toggle-charts-sql-panel":
|
|
625
|
+
toggleChartsSqlPanel();
|
|
626
|
+
return;
|
|
627
|
+
case "toggle-charts-results-panel":
|
|
628
|
+
toggleChartsResultsPanel();
|
|
629
|
+
return;
|
|
630
|
+
case "set-charts-height-preset":
|
|
631
|
+
if (actionNode.dataset.preset) {
|
|
632
|
+
setChartsHeightPreset(actionNode.dataset.preset);
|
|
633
|
+
}
|
|
634
|
+
return;
|
|
635
|
+
case "export-query-chart-png":
|
|
636
|
+
if (actionNode.dataset.chartId && !exportQueryChartAsPng(actionNode.dataset.chartId)) {
|
|
637
|
+
showToast("The selected chart is not ready for PNG export.", "alert");
|
|
638
|
+
}
|
|
639
|
+
return;
|
|
411
640
|
case "delete-query-history":
|
|
412
641
|
if (
|
|
413
642
|
actionNode.dataset.historyId &&
|
|
@@ -431,11 +660,61 @@ async function handleAction(actionNode) {
|
|
|
431
660
|
case "export-data-csv":
|
|
432
661
|
await exportCurrentDataTableCsv();
|
|
433
662
|
return;
|
|
663
|
+
case "toggle-data-tables":
|
|
664
|
+
toggleDataTablesPanel();
|
|
665
|
+
return;
|
|
434
666
|
case "select-structure-entry":
|
|
435
667
|
if (actionNode.dataset.entryName) {
|
|
436
668
|
await selectStructureEntry(actionNode.dataset.entryName);
|
|
437
669
|
}
|
|
438
670
|
return;
|
|
671
|
+
case "add-table-designer-column":
|
|
672
|
+
{
|
|
673
|
+
const columnId = addCurrentTableDesignerColumn();
|
|
674
|
+
if (columnId) {
|
|
675
|
+
window.requestAnimationFrame(() => {
|
|
676
|
+
focusTableDesignerColumnNameField(columnId);
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
681
|
+
case "remove-table-designer-column":
|
|
682
|
+
if (actionNode.dataset.columnId) {
|
|
683
|
+
removeCurrentTableDesignerColumn(actionNode.dataset.columnId);
|
|
684
|
+
}
|
|
685
|
+
return;
|
|
686
|
+
case "save-table-designer": {
|
|
687
|
+
const savedTableName = await saveCurrentTableDesignerDraft();
|
|
688
|
+
if (savedTableName) {
|
|
689
|
+
router.navigate(`/table-designer/${encodeURIComponent(savedTableName)}`);
|
|
690
|
+
}
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
case "copy-table-designer-sql": {
|
|
694
|
+
const sqlPreview = getState().tableDesigner.draft?.sqlPreview ?? "";
|
|
695
|
+
if (!sqlPreview.trim()) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
try {
|
|
700
|
+
await navigator.clipboard.writeText(sqlPreview);
|
|
701
|
+
showToast("SQL preview copied.", "success");
|
|
702
|
+
} catch (error) {
|
|
703
|
+
showToast("Clipboard access failed.", "alert");
|
|
704
|
+
}
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
case "import-table-designer-csv": {
|
|
708
|
+
const fileInput = document.querySelector('[data-bind="table-designer-import-file"]');
|
|
709
|
+
|
|
710
|
+
if (!(fileInput instanceof HTMLInputElement)) {
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
fileInput.value = "";
|
|
715
|
+
fileInput.click();
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
439
718
|
case "select-data-row":
|
|
440
719
|
if (actionNode.dataset.rowIndex) {
|
|
441
720
|
selectDataRow(actionNode.dataset.rowIndex);
|
|
@@ -554,8 +833,36 @@ document.addEventListener("input", (event) => {
|
|
|
554
833
|
return;
|
|
555
834
|
}
|
|
556
835
|
|
|
836
|
+
if (bindNode.dataset.bind === "table-designer-search") {
|
|
837
|
+
setTableDesignerSearchQuery(bindNode.value);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (bindNode.dataset.bind === "table-designer-field") {
|
|
842
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === "checkbox") {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (bindNode.dataset.bind === "table-designer-column-field") {
|
|
851
|
+
updateCurrentTableDesignerColumnField(
|
|
852
|
+
bindNode.dataset.columnId,
|
|
853
|
+
bindNode.dataset.field,
|
|
854
|
+
bindNode.value
|
|
855
|
+
);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
|
|
557
859
|
if (bindNode.dataset.bind === "query-history-search") {
|
|
558
860
|
setQueryHistorySearchInput(bindNode.value);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
if (bindNode.dataset.bind === "query-chart-draft:name") {
|
|
865
|
+
updateCurrentQueryChartDraftField("name", bindNode.value);
|
|
559
866
|
}
|
|
560
867
|
});
|
|
561
868
|
|
|
@@ -582,6 +889,61 @@ document.addEventListener("change", (event) => {
|
|
|
582
889
|
|
|
583
890
|
if (bindNode.dataset.bind === "data-search-column") {
|
|
584
891
|
setDataSearchColumn(bindNode.value);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (bindNode.dataset.bind === "table-designer-import-file") {
|
|
896
|
+
void handleTableDesignerCsvImport(bindNode);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
if (bindNode.dataset.bind === "table-designer-field") {
|
|
901
|
+
if (bindNode instanceof HTMLInputElement && bindNode.type === "checkbox") {
|
|
902
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.checked);
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (bindNode.dataset.bind === "table-designer-column-field") {
|
|
911
|
+
updateCurrentTableDesignerColumnField(
|
|
912
|
+
bindNode.dataset.columnId,
|
|
913
|
+
bindNode.dataset.field,
|
|
914
|
+
bindNode.value
|
|
915
|
+
);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (bindNode.dataset.bind === "table-designer-column-flag") {
|
|
920
|
+
updateCurrentTableDesignerColumnField(
|
|
921
|
+
bindNode.dataset.columnId,
|
|
922
|
+
bindNode.dataset.field,
|
|
923
|
+
bindNode.checked
|
|
924
|
+
);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
if (bindNode.dataset.bind === "query-chart-draft:chartType") {
|
|
929
|
+
updateCurrentQueryChartDraftField("chartType", bindNode.value);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if (bindNode.dataset.bind === "query-chart-draft:tableVisible") {
|
|
934
|
+
updateCurrentQueryChartDraftField("tableVisible", bindNode.checked);
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
if (bindNode.dataset.bind?.startsWith("query-chart-draft-config:")) {
|
|
939
|
+
const field = bindNode.dataset.bind.slice("query-chart-draft-config:".length);
|
|
940
|
+
const value =
|
|
941
|
+
bindNode instanceof HTMLInputElement && bindNode.type === "checkbox"
|
|
942
|
+
? bindNode.checked
|
|
943
|
+
: bindNode.value === ""
|
|
944
|
+
? null
|
|
945
|
+
: bindNode.value;
|
|
946
|
+
updateCurrentQueryChartDraftConfigField(field, value);
|
|
585
947
|
}
|
|
586
948
|
});
|
|
587
949
|
|
|
@@ -667,6 +1029,12 @@ document.addEventListener("submit", async (event) => {
|
|
|
667
1029
|
case "delete-row-confirm":
|
|
668
1030
|
await submitDeleteRowConfirmation();
|
|
669
1031
|
return;
|
|
1032
|
+
case "save-query-chart":
|
|
1033
|
+
await saveCurrentQueryChartDraft();
|
|
1034
|
+
return;
|
|
1035
|
+
case "delete-query-chart":
|
|
1036
|
+
await submitDeleteChartConfirmation();
|
|
1037
|
+
return;
|
|
670
1038
|
case "save-data-row": {
|
|
671
1039
|
const values = {};
|
|
672
1040
|
|