sqlite-hub 0.11.1 → 0.16.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 +52 -13
- package/database.sqlite +0 -0
- package/frontend/js/api.js +64 -12
- package/frontend/js/app.js +723 -37
- package/frontend/js/components/emptyState.js +42 -46
- package/frontend/js/components/modal.js +526 -2
- package/frontend/js/components/queryEditor.js +12 -3
- package/frontend/js/components/queryResults.js +79 -17
- package/frontend/js/components/rowEditorPanel.js +345 -12
- package/frontend/js/components/sidebar.js +106 -23
- package/frontend/js/components/tableDesignerEditor.js +69 -11
- package/frontend/js/store.js +437 -26
- package/frontend/js/utils/copyColumnExport.js +117 -0
- package/frontend/js/utils/exportFilenames.js +32 -0
- package/frontend/js/utils/filePathPreview.js +315 -0
- package/frontend/js/utils/format.js +1 -1
- package/frontend/js/utils/rowEditorJson.js +65 -0
- package/frontend/js/utils/sqlFormatter.js +691 -0
- package/frontend/js/utils/tableDesigner.js +178 -6
- package/frontend/js/utils/textCellStats.js +20 -0
- package/frontend/js/utils/timestampPreview.js +264 -0
- package/frontend/js/views/charts.js +3 -1
- package/frontend/js/views/data.js +39 -4
- package/frontend/js/views/editor.js +50 -1
- package/frontend/js/views/settings.js +1 -4
- package/frontend/js/views/structure.js +154 -212
- package/frontend/styles/base.css +6 -0
- package/frontend/styles/components.css +463 -2
- package/frontend/styles/structure-graph.css +0 -3
- package/frontend/styles/tailwind.generated.css +1 -1
- package/frontend/styles/tokens.css +95 -95
- package/package.json +2 -3
- package/server/routes/export.js +97 -12
- package/server/services/sqlite/dataBrowserService.js +2 -68
- package/server/services/sqlite/exportService.js +74 -15
- package/server/services/sqlite/introspection.js +209 -1
- package/server/services/sqlite/sqlExecutor.js +31 -0
- package/server/services/sqlite/tableDesigner/changeAnalysis.js +25 -1
- package/server/services/sqlite/tableDesigner/schemaMapping.js +105 -10
- package/server/services/sqlite/tableDesigner/validation.js +60 -2
- package/server/services/sqlite/tableDesignerService.js +1 -1
- package/server/services/sqlite/tableFilter.js +75 -0
- package/server/utils/csv.js +30 -4
- package/tests/check-constraint-options.test.js +90 -0
- package/tests/export-filenames.test.js +34 -0
- package/tests/file-path-preview.test.js +165 -0
- package/tests/row-editor-json.test.js +82 -0
- package/tests/row-editor-timestamp-preview.test.js +192 -0
- package/tests/sql-formatter.test.js +173 -0
- package/tests/sql-highlight.test.js +38 -0
- package/tests/table-designer-v2-unique-constraints.test.js +78 -0
- package/tests/text-cell-stats.test.js +38 -0
package/frontend/js/app.js
CHANGED
|
@@ -33,8 +33,10 @@ import {
|
|
|
33
33
|
dismissMediaTaggingIssue,
|
|
34
34
|
dismissToast,
|
|
35
35
|
executeCurrentQuery,
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
duplicateCurrentDataTableAsTable,
|
|
37
|
+
exportCurrentDataTableFormat,
|
|
38
|
+
duplicateCurrentQueryAsTable,
|
|
39
|
+
exportCurrentQueryFormat,
|
|
38
40
|
getState,
|
|
39
41
|
initializeApp,
|
|
40
42
|
loadMoreQueryHistory,
|
|
@@ -45,10 +47,14 @@ import {
|
|
|
45
47
|
openDeleteEditorRowModal,
|
|
46
48
|
openDeleteQueryHistoryModal,
|
|
47
49
|
openDeleteQueryChartModal,
|
|
50
|
+
openDataExportModal,
|
|
51
|
+
openQueryExportModal,
|
|
48
52
|
openDataRowByIdentity,
|
|
49
53
|
openEditConnectionModal,
|
|
50
54
|
openCreateQueryChartModal,
|
|
55
|
+
openCopyColumnModal,
|
|
51
56
|
openEditQueryChartModal,
|
|
57
|
+
preserveCurrentDataRowSelectionForReload,
|
|
52
58
|
openDataRowUpdatePreview,
|
|
53
59
|
openEditorRowUpdatePreview,
|
|
54
60
|
refreshCurrentRoute,
|
|
@@ -93,6 +99,8 @@ import {
|
|
|
93
99
|
sortEditorResultsByColumn,
|
|
94
100
|
setQueryHistorySearchInput,
|
|
95
101
|
setQueryHistoryTab,
|
|
102
|
+
setCopyColumnModalError,
|
|
103
|
+
setCopyColumnModalSubmitting,
|
|
96
104
|
setRoute,
|
|
97
105
|
saveQueryHistoryNotes,
|
|
98
106
|
saveQueryHistoryTitle,
|
|
@@ -103,6 +111,7 @@ import {
|
|
|
103
111
|
setMediaTaggingWorkflowMediaRotationDegrees,
|
|
104
112
|
queueTableDesignerCsvImport,
|
|
105
113
|
showToast,
|
|
114
|
+
storeCopyColumnPreferences,
|
|
106
115
|
submitCreateConnection,
|
|
107
116
|
createCurrentMediaTag,
|
|
108
117
|
submitDeleteRowConfirmation,
|
|
@@ -114,9 +123,11 @@ import {
|
|
|
114
123
|
toggleQueryHistorySavedState,
|
|
115
124
|
updateCurrentMediaTaggingField,
|
|
116
125
|
updateCurrentMediaTaggingTagFormField,
|
|
126
|
+
updateCopyColumnModalFormatField,
|
|
117
127
|
updateCurrentQueryChartDraftConfigField,
|
|
118
128
|
updateCurrentQueryChartDraftField,
|
|
119
129
|
updateCurrentTableDesignerColumnField,
|
|
130
|
+
updateCurrentTableDesignerConstraintField,
|
|
120
131
|
updateCurrentTableDesignerField,
|
|
121
132
|
addCurrentTableDesignerColumn,
|
|
122
133
|
applyCurrentMediaTaggingSelection,
|
|
@@ -132,11 +143,30 @@ import { renderOverviewView } from './views/overview.js';
|
|
|
132
143
|
import { renderSettingsView } from './views/settings.js';
|
|
133
144
|
import { renderStructureView } from './views/structure.js';
|
|
134
145
|
import { renderTableDesignerView } from './views/tableDesigner.js';
|
|
146
|
+
import { replaceChildrenFromRenderedMarkup, replaceElementFromRenderedMarkup } from './utils/dom.js';
|
|
135
147
|
import {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
148
|
+
buildCopyColumnText,
|
|
149
|
+
getCopyColumnExportMetadata,
|
|
150
|
+
isMarkdownTodoCopyColumnMode,
|
|
151
|
+
normalizeCopyColumnMode,
|
|
152
|
+
} from './utils/copyColumnExport.js';
|
|
153
|
+
import { formatNumber, highlightSql } from './utils/format.js';
|
|
154
|
+
import {
|
|
155
|
+
compactPathForDisplay,
|
|
156
|
+
detectFilePathValue,
|
|
157
|
+
getPathTypeLabel,
|
|
158
|
+
} from './utils/filePathPreview.js';
|
|
159
|
+
import { formatSqlQuery } from './utils/sqlFormatter.js';
|
|
160
|
+
import {
|
|
161
|
+
buildDataRowEditorJsonObject,
|
|
162
|
+
buildEditorRowEditorJsonObject,
|
|
163
|
+
stringifyRowEditorJson,
|
|
164
|
+
} from './utils/rowEditorJson.js';
|
|
165
|
+
import { getTimestampPreviewForField } from './utils/timestampPreview.js';
|
|
166
|
+
import {
|
|
167
|
+
formatTextCellCharacterCount,
|
|
168
|
+
getTextCellCharacterCount,
|
|
169
|
+
} from './utils/textCellStats.js';
|
|
140
170
|
|
|
141
171
|
const appRoot = document.querySelector('#app');
|
|
142
172
|
|
|
@@ -333,7 +363,7 @@ function normalizeMediaTaggingRotationDegrees(value) {
|
|
|
333
363
|
return 0;
|
|
334
364
|
}
|
|
335
365
|
|
|
336
|
-
return ((Math.round(numericValue / 90) * 90) % 360 + 360) % 360;
|
|
366
|
+
return (((Math.round(numericValue / 90) * 90) % 360) + 360) % 360;
|
|
337
367
|
}
|
|
338
368
|
|
|
339
369
|
function syncMediaTaggingMediaRotationUi(node, rotationDegrees) {
|
|
@@ -384,7 +414,9 @@ function syncMediaTaggingTagSearchUi(input) {
|
|
|
384
414
|
continue;
|
|
385
415
|
}
|
|
386
416
|
|
|
387
|
-
const searchText = String(tagOption.dataset.tagSearchText ?? '')
|
|
417
|
+
const searchText = String(tagOption.dataset.tagSearchText ?? '')
|
|
418
|
+
.trim()
|
|
419
|
+
.toLowerCase();
|
|
388
420
|
tagOption.hidden = Boolean(normalizedQuery) && !searchText.includes(normalizedQuery);
|
|
389
421
|
}
|
|
390
422
|
|
|
@@ -502,23 +534,18 @@ function syncQueryHistoryUi(historyId) {
|
|
|
502
534
|
return false;
|
|
503
535
|
}
|
|
504
536
|
|
|
505
|
-
const historyItem =
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
String(numericId),
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
)?.closest('.query-history-item');
|
|
537
|
+
const historyItem =
|
|
538
|
+
state.editor.history.find(entry => Number(entry.id) === numericId) ?? state.editor.historyDetail ?? null;
|
|
539
|
+
const listItemNode = shellRefs.view
|
|
540
|
+
.querySelector(
|
|
541
|
+
['[data-action="select-query-history-item"][data-history-id="', String(numericId), '"]'].join(''),
|
|
542
|
+
)
|
|
543
|
+
?.closest('.query-history-item');
|
|
513
544
|
|
|
514
545
|
if (historyItem && listItemNode instanceof HTMLElement) {
|
|
515
546
|
replaceElementFromRenderedMarkup(
|
|
516
547
|
listItemNode,
|
|
517
|
-
renderQueryHistoryListItem(
|
|
518
|
-
historyItem,
|
|
519
|
-
state.editor.historyActiveId,
|
|
520
|
-
state.editor.historySelectedId,
|
|
521
|
-
),
|
|
548
|
+
renderQueryHistoryListItem(historyItem, state.editor.historyActiveId, state.editor.historySelectedId),
|
|
522
549
|
);
|
|
523
550
|
}
|
|
524
551
|
|
|
@@ -1256,6 +1283,484 @@ async function executeEditorQueryAndNavigate() {
|
|
|
1256
1283
|
router.navigate(success && activeTab === 'results' ? '/editor/results' : '/editor');
|
|
1257
1284
|
}
|
|
1258
1285
|
|
|
1286
|
+
function formatCurrentQuery() {
|
|
1287
|
+
const currentQuery = getState().editor.sqlText ?? '';
|
|
1288
|
+
const formattedQuery = formatSqlQuery(currentQuery);
|
|
1289
|
+
|
|
1290
|
+
if (!formattedQuery) {
|
|
1291
|
+
showToast('No SQL query to format.', 'alert');
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
if (formattedQuery === currentQuery) {
|
|
1296
|
+
focusQueryEditorInput();
|
|
1297
|
+
showToast('SQL query is already formatted.', 'muted');
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
invalidateMainRenderCache();
|
|
1302
|
+
setCurrentQuery(formattedQuery);
|
|
1303
|
+
|
|
1304
|
+
const input = document.querySelector('[data-bind="current-query"]');
|
|
1305
|
+
|
|
1306
|
+
if (input instanceof HTMLTextAreaElement) {
|
|
1307
|
+
input.value = formattedQuery;
|
|
1308
|
+
syncQueryEditorHighlight(input);
|
|
1309
|
+
syncQueryEditorScroll(input);
|
|
1310
|
+
input.focus({ preventScroll: true });
|
|
1311
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
1312
|
+
} else {
|
|
1313
|
+
pendingQueryEditorFocus = true;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
showToast('SQL query formatted.', 'success');
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
const OPENABLE_URL_PATTERN = /^https?:\/\/[^\s<>"']+$/i;
|
|
1320
|
+
|
|
1321
|
+
function getOpenableUrl(value) {
|
|
1322
|
+
const text = String(value ?? '').trim();
|
|
1323
|
+
|
|
1324
|
+
if (!OPENABLE_URL_PATTERN.test(text)) {
|
|
1325
|
+
return null;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
try {
|
|
1329
|
+
const url = new URL(text);
|
|
1330
|
+
return ['http:', 'https:'].includes(url.protocol) ? url.href : null;
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
return null;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
function openRowEditorUrl(actionNode) {
|
|
1337
|
+
const field = actionNode.closest('[data-row-editor-url-field]');
|
|
1338
|
+
const inputValue = field?.querySelector('[data-row-editor-url-input]')?.value;
|
|
1339
|
+
const url = getOpenableUrl(inputValue ?? actionNode.dataset.url);
|
|
1340
|
+
|
|
1341
|
+
if (!url) {
|
|
1342
|
+
showToast('Field value is not a valid URL.', 'alert');
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
const link = document.createElement('a');
|
|
1347
|
+
|
|
1348
|
+
link.href = url;
|
|
1349
|
+
link.target = '_blank';
|
|
1350
|
+
link.rel = 'noopener noreferrer';
|
|
1351
|
+
document.body.appendChild(link);
|
|
1352
|
+
link.click();
|
|
1353
|
+
link.remove();
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function getExportFilenameFromAction(actionNode) {
|
|
1357
|
+
const input = actionNode.closest('[data-export-modal]')?.querySelector('input[name="filename"]');
|
|
1358
|
+
return input instanceof HTMLInputElement ? input.value : '';
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
function syncRowEditorTimestampPreview(inputNode) {
|
|
1362
|
+
const fieldNode = inputNode.closest('[data-row-editor-field]');
|
|
1363
|
+
const previewNode = fieldNode?.querySelector('[data-row-editor-timestamp-preview]');
|
|
1364
|
+
|
|
1365
|
+
if (!fieldNode || !previewNode) {
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
const columnName = fieldNode.dataset.rowEditorColumnName ?? '';
|
|
1370
|
+
const protectedKeyColumn = fieldNode.dataset.rowEditorProtectedKey === 'true';
|
|
1371
|
+
const tableMeta = {
|
|
1372
|
+
columns: [
|
|
1373
|
+
{
|
|
1374
|
+
name: columnName,
|
|
1375
|
+
primaryKeyPosition: protectedKeyColumn ? 1 : 0,
|
|
1376
|
+
foreignKey: protectedKeyColumn,
|
|
1377
|
+
},
|
|
1378
|
+
],
|
|
1379
|
+
foreignKeys: [],
|
|
1380
|
+
};
|
|
1381
|
+
const preview = getTimestampPreviewForField({
|
|
1382
|
+
columnName,
|
|
1383
|
+
value: inputNode.value,
|
|
1384
|
+
tableMeta,
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
if (preview.kind !== 'timestamp') {
|
|
1388
|
+
previewNode.hidden = true;
|
|
1389
|
+
previewNode.textContent = '';
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
previewNode.hidden = false;
|
|
1394
|
+
previewNode.textContent = `Interpretiert als Datum: ${preview.formatted}`;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function syncRowEditorCharacterCount(inputNode) {
|
|
1398
|
+
const fieldNode = inputNode.closest('[data-row-editor-field]');
|
|
1399
|
+
const countNode = fieldNode?.querySelector('[data-row-editor-char-count]');
|
|
1400
|
+
|
|
1401
|
+
if (!fieldNode || !countNode) {
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
const count = getTextCellCharacterCount(inputNode.value);
|
|
1406
|
+
|
|
1407
|
+
if (count === null) {
|
|
1408
|
+
countNode.hidden = true;
|
|
1409
|
+
countNode.textContent = '';
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
countNode.hidden = false;
|
|
1414
|
+
countNode.textContent = formatTextCellCharacterCount(count);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
function syncRowEditorFilePathPreview(inputNode) {
|
|
1418
|
+
const fieldNode = inputNode.closest('[data-row-editor-field]');
|
|
1419
|
+
const previewNode = fieldNode?.querySelector('[data-row-editor-filepath-preview]');
|
|
1420
|
+
|
|
1421
|
+
if (!fieldNode || !previewNode) {
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
const columnName = fieldNode.dataset.rowEditorColumnName ?? '';
|
|
1426
|
+
const protectedKeyColumn = fieldNode.dataset.rowEditorProtectedKey === 'true';
|
|
1427
|
+
const tableMeta = {
|
|
1428
|
+
columns: [
|
|
1429
|
+
{
|
|
1430
|
+
name: columnName,
|
|
1431
|
+
primaryKeyPosition: protectedKeyColumn ? 1 : 0,
|
|
1432
|
+
foreignKey: protectedKeyColumn,
|
|
1433
|
+
},
|
|
1434
|
+
],
|
|
1435
|
+
foreignKeys: [],
|
|
1436
|
+
};
|
|
1437
|
+
const preview = detectFilePathValue(inputNode.value, columnName, tableMeta);
|
|
1438
|
+
|
|
1439
|
+
if (!preview) {
|
|
1440
|
+
previewNode.hidden = true;
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
const filenameNode = previewNode.querySelector('[data-row-editor-filepath-filename]');
|
|
1445
|
+
const directoryRowNode = previewNode.querySelector('[data-row-editor-filepath-directory-row]');
|
|
1446
|
+
const directoryNode = previewNode.querySelector('[data-row-editor-filepath-directory]');
|
|
1447
|
+
const extensionNode = previewNode.querySelector('[data-row-editor-filepath-extension]');
|
|
1448
|
+
const typeNode = previewNode.querySelector('[data-row-editor-filepath-type]');
|
|
1449
|
+
|
|
1450
|
+
if (filenameNode) {
|
|
1451
|
+
filenameNode.textContent = preview.fileName ?? 'N/A';
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
if (directoryRowNode) {
|
|
1455
|
+
directoryRowNode.hidden = !preview.directory;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (directoryNode) {
|
|
1459
|
+
directoryNode.textContent = preview.directory ? compactPathForDisplay(preview.directory, 72) : '';
|
|
1460
|
+
directoryNode.setAttribute('title', preview.directory ?? '');
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (extensionNode) {
|
|
1464
|
+
extensionNode.textContent = preview.extension ?? 'N/A';
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
if (typeNode) {
|
|
1468
|
+
typeNode.textContent = getPathTypeLabel(preview.pathType);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
previewNode.hidden = false;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function closeCopyColumnMenus(exceptMenu = null) {
|
|
1475
|
+
document.querySelectorAll('[data-copy-column-menu][open]').forEach(menu => {
|
|
1476
|
+
if (menu !== exceptMenu && menu instanceof HTMLDetailsElement) {
|
|
1477
|
+
menu.open = false;
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function closeSidebarDatabasePickers(exceptPicker = null) {
|
|
1483
|
+
document.querySelectorAll('.sidebar-db-picker[open]').forEach(picker => {
|
|
1484
|
+
if (picker !== exceptPicker && picker instanceof HTMLDetailsElement) {
|
|
1485
|
+
picker.open = false;
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function openCopyColumnMenu(headerNode) {
|
|
1491
|
+
const menu = headerNode?.querySelector('[data-copy-column-menu]');
|
|
1492
|
+
|
|
1493
|
+
if (!(menu instanceof HTMLDetailsElement)) {
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
closeCopyColumnMenus(menu);
|
|
1498
|
+
menu.open = true;
|
|
1499
|
+
menu.querySelector('summary')?.focus({ preventScroll: true });
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
function getCopyColumnResult(state, scope) {
|
|
1503
|
+
return scope === 'charts' ? state.charts.result : state.editor.result;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
function slugifyExportFilenamePart(value, fallback = 'column') {
|
|
1507
|
+
const slug = String(value ?? '')
|
|
1508
|
+
.trim()
|
|
1509
|
+
.toLowerCase()
|
|
1510
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
1511
|
+
.replace(/^-+|-+$/g, '');
|
|
1512
|
+
|
|
1513
|
+
return slug || fallback;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function buildCopyColumnExportFilename(columnName, copyMode) {
|
|
1517
|
+
const metadata = getCopyColumnExportMetadata(copyMode);
|
|
1518
|
+
const columnSlug = slugifyExportFilenamePart(columnName);
|
|
1519
|
+
|
|
1520
|
+
return `${columnSlug}-${metadata.suffix}.${metadata.extension}`;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function downloadTextFile({ text, filename, mimeType }) {
|
|
1524
|
+
const blob = new Blob([text], { type: mimeType });
|
|
1525
|
+
const url = URL.createObjectURL(blob);
|
|
1526
|
+
const link = document.createElement('a');
|
|
1527
|
+
|
|
1528
|
+
link.href = url;
|
|
1529
|
+
link.download = filename;
|
|
1530
|
+
document.body.appendChild(link);
|
|
1531
|
+
link.click();
|
|
1532
|
+
link.remove();
|
|
1533
|
+
URL.revokeObjectURL(url);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
function getSelectedDataBrowserRowForJson(state) {
|
|
1537
|
+
if (state.dataBrowser.selectedRow) {
|
|
1538
|
+
return state.dataBrowser.selectedRow;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
const rowIndex = state.dataBrowser.selectedRowIndex;
|
|
1542
|
+
|
|
1543
|
+
if (typeof rowIndex !== 'number') {
|
|
1544
|
+
return null;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
return state.dataBrowser.table?.rows?.[rowIndex] ?? null;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
function getSelectedEditorResultRowForJson(state) {
|
|
1551
|
+
const rowIndex = state.editor.selectedRowIndex;
|
|
1552
|
+
|
|
1553
|
+
if (typeof rowIndex !== 'number') {
|
|
1554
|
+
return null;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
return state.editor.result?.rows?.[rowIndex] ?? null;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function applyChangedRowEditorFormValues(rowObject) {
|
|
1561
|
+
const form = shellRefs.panel.querySelector(
|
|
1562
|
+
'form[data-form="save-data-row"], form[data-form="save-editor-row"]',
|
|
1563
|
+
);
|
|
1564
|
+
|
|
1565
|
+
if (!form) {
|
|
1566
|
+
return rowObject;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
const nextRowObject = { ...rowObject };
|
|
1570
|
+
const formData = new FormData(form);
|
|
1571
|
+
|
|
1572
|
+
for (const [key, value] of formData.entries()) {
|
|
1573
|
+
if (!key.startsWith('field:')) {
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
const control = Array.from(form.elements).find(element => element.name === key);
|
|
1578
|
+
const initialValue = control?.dataset?.rowEditorInitialValue;
|
|
1579
|
+
const currentValue = String(value ?? '');
|
|
1580
|
+
|
|
1581
|
+
if (initialValue !== undefined && currentValue === initialValue) {
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
nextRowObject[key.slice('field:'.length)] = currentValue;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
return nextRowObject;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function buildRowEditorJsonPayload(state) {
|
|
1592
|
+
if (state.route.name === 'data') {
|
|
1593
|
+
const row = getSelectedDataBrowserRowForJson(state);
|
|
1594
|
+
const table = state.dataBrowser.table;
|
|
1595
|
+
const tableName = table?.name ?? state.dataBrowser.selectedTable ?? 'row';
|
|
1596
|
+
|
|
1597
|
+
if (!row || !table) {
|
|
1598
|
+
return null;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
const rowObject = applyChangedRowEditorFormValues(
|
|
1602
|
+
buildDataRowEditorJsonObject({
|
|
1603
|
+
row,
|
|
1604
|
+
columns: table.columns ?? table.columnMeta ?? [],
|
|
1605
|
+
}),
|
|
1606
|
+
);
|
|
1607
|
+
|
|
1608
|
+
return {
|
|
1609
|
+
filename: `${slugifyExportFilenamePart(tableName, 'row')}-row.json`,
|
|
1610
|
+
label: tableName,
|
|
1611
|
+
text: stringifyRowEditorJson(rowObject),
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
if (state.route.name === 'editorResults') {
|
|
1616
|
+
const row = getSelectedEditorResultRowForJson(state);
|
|
1617
|
+
const result = state.editor.result;
|
|
1618
|
+
const rowIndex = state.editor.selectedRowIndex;
|
|
1619
|
+
const tableName = result?.editing?.tableName ?? 'query-result';
|
|
1620
|
+
|
|
1621
|
+
if (!row || !result) {
|
|
1622
|
+
return null;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
const rowObject = applyChangedRowEditorFormValues(
|
|
1626
|
+
buildEditorRowEditorJsonObject({
|
|
1627
|
+
row,
|
|
1628
|
+
editingColumns: result.editing?.columns ?? [],
|
|
1629
|
+
resultColumns: result.columns ?? [],
|
|
1630
|
+
}),
|
|
1631
|
+
);
|
|
1632
|
+
|
|
1633
|
+
return {
|
|
1634
|
+
filename: `${slugifyExportFilenamePart(tableName, 'query-result')}-row-${Number(rowIndex) + 1}.json`,
|
|
1635
|
+
label: tableName,
|
|
1636
|
+
text: stringifyRowEditorJson(rowObject),
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
async function copyRowEditorJson() {
|
|
1644
|
+
const payload = buildRowEditorJsonPayload(getState());
|
|
1645
|
+
|
|
1646
|
+
if (!payload) {
|
|
1647
|
+
showToast('No row is selected.', 'alert');
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
if (!navigator.clipboard?.writeText) {
|
|
1652
|
+
showToast('Clipboard API is not available.', 'alert');
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
try {
|
|
1657
|
+
await navigator.clipboard.writeText(payload.text);
|
|
1658
|
+
showToast(`Row from "${payload.label}" copied as JSON.`, 'success');
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
showToast('Row JSON could not be copied.', 'alert');
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
function exportRowEditorJson() {
|
|
1665
|
+
const payload = buildRowEditorJsonPayload(getState());
|
|
1666
|
+
|
|
1667
|
+
if (!payload) {
|
|
1668
|
+
showToast('No row is selected.', 'alert');
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
downloadTextFile({
|
|
1673
|
+
text: payload.text,
|
|
1674
|
+
filename: payload.filename,
|
|
1675
|
+
mimeType: 'application/json;charset=utf-8',
|
|
1676
|
+
});
|
|
1677
|
+
showToast(`Row from "${payload.label}" exported as JSON.`, 'success');
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function preserveDataRowEditorSelectionForReload() {
|
|
1681
|
+
preserveCurrentDataRowSelectionForReload();
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
async function submitCopyColumnModal(formData) {
|
|
1685
|
+
const state = getState();
|
|
1686
|
+
const modal = state.modal;
|
|
1687
|
+
|
|
1688
|
+
if (modal?.kind !== 'copy-column') {
|
|
1689
|
+
return;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
const scope = String(formData.get('scope') ?? modal.scope ?? 'editor');
|
|
1693
|
+
const columnName = String(formData.get('columnName') ?? modal.columnName ?? '');
|
|
1694
|
+
const copyMode = normalizeCopyColumnMode(String(formData.get('copyMode') ?? modal.copyMode ?? 'column'));
|
|
1695
|
+
const separator = String(formData.get('separator') ?? ',');
|
|
1696
|
+
const wrapper = String(formData.get('wrapper') ?? '');
|
|
1697
|
+
const lineBreaks = formData.get('lineBreaks') === 'on';
|
|
1698
|
+
const outputSeparator = lineBreaks ? '\n' : separator;
|
|
1699
|
+
const intent = String(formData.get('intent') ?? 'copy');
|
|
1700
|
+
const isExportIntent = intent === 'export';
|
|
1701
|
+
const result = getCopyColumnResult(state, scope);
|
|
1702
|
+
const hasColumn = (result?.columns ?? []).some(column => String(column) === columnName);
|
|
1703
|
+
|
|
1704
|
+
if (!hasColumn) {
|
|
1705
|
+
setCopyColumnModalError({
|
|
1706
|
+
code: 'COPY_COLUMN_UNAVAILABLE',
|
|
1707
|
+
message: 'Column is no longer available in the current result set.',
|
|
1708
|
+
});
|
|
1709
|
+
showToast('Column could not be copied.', 'alert');
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
if (!isMarkdownTodoCopyColumnMode(copyMode)) {
|
|
1714
|
+
storeCopyColumnPreferences({ separator, wrapper, lineBreaks });
|
|
1715
|
+
}
|
|
1716
|
+
setCopyColumnModalSubmitting(true);
|
|
1717
|
+
|
|
1718
|
+
const { text, valueCount } = buildCopyColumnText({
|
|
1719
|
+
result,
|
|
1720
|
+
columnName,
|
|
1721
|
+
copyMode,
|
|
1722
|
+
separator: outputSeparator,
|
|
1723
|
+
wrapper,
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
try {
|
|
1727
|
+
if (isExportIntent) {
|
|
1728
|
+
const metadata = getCopyColumnExportMetadata(copyMode);
|
|
1729
|
+
|
|
1730
|
+
downloadTextFile({
|
|
1731
|
+
text,
|
|
1732
|
+
filename: buildCopyColumnExportFilename(columnName, copyMode),
|
|
1733
|
+
mimeType: metadata.mimeType,
|
|
1734
|
+
});
|
|
1735
|
+
closeModal();
|
|
1736
|
+
showToast(
|
|
1737
|
+
`Column "${columnName}" exported as ${metadata.label} · ${formatNumber(valueCount)} ${
|
|
1738
|
+
valueCount === 1 ? 'value' : 'values'
|
|
1739
|
+
}`,
|
|
1740
|
+
'success',
|
|
1741
|
+
);
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
if (!navigator.clipboard?.writeText) {
|
|
1746
|
+
throw new Error('Clipboard API is not available.');
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
await navigator.clipboard.writeText(text);
|
|
1750
|
+
closeModal();
|
|
1751
|
+
showToast(
|
|
1752
|
+
`Column "${columnName}" copied · ${formatNumber(valueCount)} ${valueCount === 1 ? 'value' : 'values'}`,
|
|
1753
|
+
'success',
|
|
1754
|
+
);
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
setCopyColumnModalError({
|
|
1757
|
+
code: isExportIntent ? 'COPY_COLUMN_EXPORT_FAILED' : 'CLIPBOARD_ACCESS_FAILED',
|
|
1758
|
+
message: error?.message || (isExportIntent ? 'Column export failed.' : 'Clipboard access failed.'),
|
|
1759
|
+
});
|
|
1760
|
+
showToast(isExportIntent ? 'Column export failed.' : 'Clipboard access failed.', 'alert');
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1259
1764
|
async function handleAction(actionNode) {
|
|
1260
1765
|
const { action } = actionNode.dataset;
|
|
1261
1766
|
|
|
@@ -1266,8 +1771,28 @@ async function handleAction(actionNode) {
|
|
|
1266
1771
|
case 'refresh-view':
|
|
1267
1772
|
await refreshCurrentRoute();
|
|
1268
1773
|
return;
|
|
1774
|
+
case 'open-row-editor-url':
|
|
1775
|
+
openRowEditorUrl(actionNode);
|
|
1776
|
+
return;
|
|
1777
|
+
case 'copy-row-editor-json':
|
|
1778
|
+
await copyRowEditorJson();
|
|
1779
|
+
return;
|
|
1780
|
+
case 'export-row-editor-json':
|
|
1781
|
+
exportRowEditorJson();
|
|
1782
|
+
return;
|
|
1269
1783
|
case 'open-modal':
|
|
1270
|
-
openModal(actionNode.dataset.modal
|
|
1784
|
+
openModal(actionNode.dataset.modal, {
|
|
1785
|
+
columnId: actionNode.dataset.columnId,
|
|
1786
|
+
columnName: actionNode.dataset.columnName,
|
|
1787
|
+
});
|
|
1788
|
+
return;
|
|
1789
|
+
case 'open-copy-column-modal':
|
|
1790
|
+
closeCopyColumnMenus();
|
|
1791
|
+
openCopyColumnModal({
|
|
1792
|
+
scope: actionNode.dataset.resultScope,
|
|
1793
|
+
columnName: actionNode.dataset.columnName ?? '',
|
|
1794
|
+
mode: actionNode.dataset.copyMode,
|
|
1795
|
+
});
|
|
1271
1796
|
return;
|
|
1272
1797
|
case 'open-create-query-chart-modal':
|
|
1273
1798
|
openCreateQueryChartModal();
|
|
@@ -1291,6 +1816,7 @@ async function handleAction(actionNode) {
|
|
|
1291
1816
|
dismissToast(actionNode.dataset.toastId);
|
|
1292
1817
|
return;
|
|
1293
1818
|
case 'select-connection': {
|
|
1819
|
+
closeSidebarDatabasePickers();
|
|
1294
1820
|
resetStructureGraphForDatabaseChange();
|
|
1295
1821
|
const next = await selectConnection(actionNode.dataset.connectionId);
|
|
1296
1822
|
if (next) {
|
|
@@ -1346,6 +1872,9 @@ async function handleAction(actionNode) {
|
|
|
1346
1872
|
await executeEditorQueryAndNavigate();
|
|
1347
1873
|
return;
|
|
1348
1874
|
}
|
|
1875
|
+
case 'format-current-query':
|
|
1876
|
+
formatCurrentQuery();
|
|
1877
|
+
return;
|
|
1349
1878
|
case 'delete-data-row':
|
|
1350
1879
|
openDeleteDataRowModal(actionNode.dataset.rowIndex);
|
|
1351
1880
|
return;
|
|
@@ -1464,12 +1993,46 @@ async function handleAction(actionNode) {
|
|
|
1464
1993
|
router.navigate(tab === 'results' ? '/editor/results' : '/editor');
|
|
1465
1994
|
return;
|
|
1466
1995
|
}
|
|
1467
|
-
case '
|
|
1468
|
-
|
|
1996
|
+
case 'open-query-export-modal':
|
|
1997
|
+
openQueryExportModal();
|
|
1998
|
+
return;
|
|
1999
|
+
case 'export-query-format': {
|
|
2000
|
+
const format = actionNode.dataset.exportFormat;
|
|
2001
|
+
const filename = getExportFilenameFromAction(actionNode);
|
|
2002
|
+
|
|
2003
|
+
if (format === 'table') {
|
|
2004
|
+
const imported = await duplicateCurrentQueryAsTable(filename);
|
|
2005
|
+
|
|
2006
|
+
if (imported) {
|
|
2007
|
+
router.navigate('/table-designer/new');
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
await exportCurrentQueryFormat(format, filename);
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
case 'open-data-export-modal':
|
|
2017
|
+
openDataExportModal();
|
|
1469
2018
|
return;
|
|
1470
|
-
case 'export-data-
|
|
1471
|
-
|
|
2019
|
+
case 'export-data-format': {
|
|
2020
|
+
const format = actionNode.dataset.exportFormat;
|
|
2021
|
+
const filename = getExportFilenameFromAction(actionNode);
|
|
2022
|
+
|
|
2023
|
+
if (format === 'table') {
|
|
2024
|
+
const imported = await duplicateCurrentDataTableAsTable(filename);
|
|
2025
|
+
|
|
2026
|
+
if (imported) {
|
|
2027
|
+
router.navigate('/table-designer/new');
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
await exportCurrentDataTableFormat(format, filename);
|
|
1472
2034
|
return;
|
|
2035
|
+
}
|
|
1473
2036
|
case 'toggle-data-tables':
|
|
1474
2037
|
toggleDataTablesPanel();
|
|
1475
2038
|
return;
|
|
@@ -1565,11 +2128,7 @@ async function handleAction(actionNode) {
|
|
|
1565
2128
|
const currentRotation = getState().mediaTagging.workflowMediaRotationDegrees ?? 0;
|
|
1566
2129
|
const command = actionNode.dataset.rotationCommand;
|
|
1567
2130
|
const nextRotation =
|
|
1568
|
-
command === 'left'
|
|
1569
|
-
? currentRotation - 90
|
|
1570
|
-
: command === 'right'
|
|
1571
|
-
? currentRotation + 90
|
|
1572
|
-
: 0;
|
|
2131
|
+
command === 'left' ? currentRotation - 90 : command === 'right' ? currentRotation + 90 : 0;
|
|
1573
2132
|
|
|
1574
2133
|
syncMediaTaggingMediaRotationUi(actionNode, nextRotation);
|
|
1575
2134
|
setMediaTaggingWorkflowMediaRotationDegrees(nextRotation, { notify: false });
|
|
@@ -1649,6 +2208,7 @@ async function handleAction(actionNode) {
|
|
|
1649
2208
|
}
|
|
1650
2209
|
return;
|
|
1651
2210
|
case 'reload-data-route':
|
|
2211
|
+
preserveDataRowEditorSelectionForReload();
|
|
1652
2212
|
await refreshCurrentRoute();
|
|
1653
2213
|
return;
|
|
1654
2214
|
default:
|
|
@@ -1669,7 +2229,37 @@ function canApplyMediaTaggingShortcut(state) {
|
|
|
1669
2229
|
}
|
|
1670
2230
|
|
|
1671
2231
|
document.addEventListener('click', event => {
|
|
1672
|
-
const
|
|
2232
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2233
|
+
|
|
2234
|
+
if (!target) {
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
const copyColumnMenu = target.closest('[data-copy-column-menu]');
|
|
2239
|
+
|
|
2240
|
+
if (!copyColumnMenu) {
|
|
2241
|
+
closeCopyColumnMenus();
|
|
2242
|
+
} else if (target.closest('.query-result-column-menu__toggle')) {
|
|
2243
|
+
window.requestAnimationFrame(() => {
|
|
2244
|
+
if (copyColumnMenu instanceof HTMLDetailsElement && copyColumnMenu.open) {
|
|
2245
|
+
closeCopyColumnMenus(copyColumnMenu);
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
const sidebarDatabasePicker = target.closest('.sidebar-db-picker');
|
|
2251
|
+
|
|
2252
|
+
if (!sidebarDatabasePicker) {
|
|
2253
|
+
closeSidebarDatabasePickers();
|
|
2254
|
+
} else if (target.closest('.sidebar-footer-card')) {
|
|
2255
|
+
window.requestAnimationFrame(() => {
|
|
2256
|
+
if (sidebarDatabasePicker instanceof HTMLDetailsElement && sidebarDatabasePicker.open) {
|
|
2257
|
+
closeSidebarDatabasePickers(sidebarDatabasePicker);
|
|
2258
|
+
}
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
const actionNode = target.closest('[data-action]');
|
|
1673
2263
|
|
|
1674
2264
|
if (!actionNode) {
|
|
1675
2265
|
return;
|
|
@@ -1678,6 +2268,18 @@ document.addEventListener('click', event => {
|
|
|
1678
2268
|
handleAction(actionNode);
|
|
1679
2269
|
});
|
|
1680
2270
|
|
|
2271
|
+
document.addEventListener('contextmenu', event => {
|
|
2272
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2273
|
+
const headerNode = target?.closest('[data-result-column-header]');
|
|
2274
|
+
|
|
2275
|
+
if (!headerNode) {
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
event.preventDefault();
|
|
2280
|
+
openCopyColumnMenu(headerNode);
|
|
2281
|
+
});
|
|
2282
|
+
|
|
1681
2283
|
document.addEventListener('keydown', event => {
|
|
1682
2284
|
const target = event.target;
|
|
1683
2285
|
const state = getState();
|
|
@@ -1744,6 +2346,18 @@ document.addEventListener('keydown', event => {
|
|
|
1744
2346
|
return;
|
|
1745
2347
|
}
|
|
1746
2348
|
|
|
2349
|
+
if (document.querySelector('[data-copy-column-menu][open]')) {
|
|
2350
|
+
event.preventDefault();
|
|
2351
|
+
closeCopyColumnMenus();
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
if (document.querySelector('.sidebar-db-picker[open]')) {
|
|
2356
|
+
event.preventDefault();
|
|
2357
|
+
closeSidebarDatabasePickers();
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
|
|
1747
2361
|
if (
|
|
1748
2362
|
state.route.name === 'data' &&
|
|
1749
2363
|
(typeof state.dataBrowser.selectedRowIndex === 'number' || Boolean(state.dataBrowser.selectedRow))
|
|
@@ -1760,12 +2374,33 @@ document.addEventListener('keydown', event => {
|
|
|
1760
2374
|
});
|
|
1761
2375
|
|
|
1762
2376
|
document.addEventListener('input', event => {
|
|
2377
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2378
|
+
const timestampInput = target?.closest('[data-row-editor-timestamp-source]');
|
|
2379
|
+
const textCellInput = target?.closest('[data-row-editor-text-source]');
|
|
2380
|
+
|
|
2381
|
+
if (timestampInput) {
|
|
2382
|
+
syncRowEditorTimestampPreview(timestampInput);
|
|
2383
|
+
syncRowEditorFilePathPreview(timestampInput);
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
if (textCellInput) {
|
|
2387
|
+
syncRowEditorCharacterCount(textCellInput);
|
|
2388
|
+
}
|
|
2389
|
+
|
|
1763
2390
|
const bindNode = event.target.closest('[data-bind]');
|
|
1764
2391
|
|
|
1765
2392
|
if (!bindNode) {
|
|
1766
2393
|
return;
|
|
1767
2394
|
}
|
|
1768
2395
|
|
|
2396
|
+
if (bindNode.dataset.bind === 'copy-column-format-field') {
|
|
2397
|
+
updateCopyColumnModalFormatField(
|
|
2398
|
+
bindNode.dataset.field,
|
|
2399
|
+
bindNode instanceof HTMLInputElement && bindNode.type === 'checkbox' ? bindNode.checked : bindNode.value,
|
|
2400
|
+
);
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
|
|
1769
2404
|
if (bindNode.dataset.bind === 'current-query') {
|
|
1770
2405
|
invalidateMainRenderCache();
|
|
1771
2406
|
syncQueryEditorHighlight(bindNode);
|
|
@@ -1807,7 +2442,12 @@ document.addEventListener('input', event => {
|
|
|
1807
2442
|
return;
|
|
1808
2443
|
}
|
|
1809
2444
|
|
|
1810
|
-
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
2445
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value, { notify: false });
|
|
2446
|
+
|
|
2447
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2448
|
+
renderApp(getState());
|
|
2449
|
+
}
|
|
2450
|
+
|
|
1811
2451
|
return;
|
|
1812
2452
|
}
|
|
1813
2453
|
|
|
@@ -1816,7 +2456,14 @@ document.addEventListener('input', event => {
|
|
|
1816
2456
|
return;
|
|
1817
2457
|
}
|
|
1818
2458
|
|
|
1819
|
-
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value
|
|
2459
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value, {
|
|
2460
|
+
notify: false,
|
|
2461
|
+
});
|
|
2462
|
+
|
|
2463
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2464
|
+
renderApp(getState());
|
|
2465
|
+
}
|
|
2466
|
+
|
|
1820
2467
|
return;
|
|
1821
2468
|
}
|
|
1822
2469
|
|
|
@@ -1877,6 +2524,19 @@ document.addEventListener(
|
|
|
1877
2524
|
);
|
|
1878
2525
|
|
|
1879
2526
|
document.addEventListener('change', event => {
|
|
2527
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
2528
|
+
const timestampInput = target?.closest('[data-row-editor-timestamp-source]');
|
|
2529
|
+
const textCellInput = target?.closest('[data-row-editor-text-source]');
|
|
2530
|
+
|
|
2531
|
+
if (timestampInput) {
|
|
2532
|
+
syncRowEditorTimestampPreview(timestampInput);
|
|
2533
|
+
syncRowEditorFilePathPreview(timestampInput);
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
if (textCellInput) {
|
|
2537
|
+
syncRowEditorCharacterCount(textCellInput);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
1880
2540
|
const bindNode = event.target.closest('[data-bind]');
|
|
1881
2541
|
|
|
1882
2542
|
if (!bindNode) {
|
|
@@ -1935,7 +2595,12 @@ document.addEventListener('change', event => {
|
|
|
1935
2595
|
return;
|
|
1936
2596
|
}
|
|
1937
2597
|
|
|
1938
|
-
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value);
|
|
2598
|
+
updateCurrentTableDesignerField(bindNode.dataset.field, bindNode.value, { notify: false });
|
|
2599
|
+
|
|
2600
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2601
|
+
renderApp(getState());
|
|
2602
|
+
}
|
|
2603
|
+
|
|
1939
2604
|
return;
|
|
1940
2605
|
}
|
|
1941
2606
|
|
|
@@ -1952,7 +2617,14 @@ document.addEventListener('change', event => {
|
|
|
1952
2617
|
return;
|
|
1953
2618
|
}
|
|
1954
2619
|
|
|
1955
|
-
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value
|
|
2620
|
+
updateCurrentTableDesignerColumnField(bindNode.dataset.columnId, bindNode.dataset.field, bindNode.value, {
|
|
2621
|
+
notify: false,
|
|
2622
|
+
});
|
|
2623
|
+
|
|
2624
|
+
if (!syncTableDesignerDraftUi(bindNode)) {
|
|
2625
|
+
renderApp(getState());
|
|
2626
|
+
}
|
|
2627
|
+
|
|
1956
2628
|
return;
|
|
1957
2629
|
}
|
|
1958
2630
|
|
|
@@ -1961,6 +2633,16 @@ document.addEventListener('change', event => {
|
|
|
1961
2633
|
return;
|
|
1962
2634
|
}
|
|
1963
2635
|
|
|
2636
|
+
if (bindNode.dataset.bind === 'table-designer-constraint-field') {
|
|
2637
|
+
updateCurrentTableDesignerConstraintField(
|
|
2638
|
+
bindNode.dataset.constraintKind,
|
|
2639
|
+
bindNode.dataset.constraintId,
|
|
2640
|
+
bindNode.dataset.field,
|
|
2641
|
+
bindNode.value,
|
|
2642
|
+
);
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
2645
|
+
|
|
1964
2646
|
if (bindNode.dataset.bind === 'query-chart-draft:chartType') {
|
|
1965
2647
|
updateCurrentQueryChartDraftField('chartType', bindNode.value);
|
|
1966
2648
|
return;
|
|
@@ -1991,7 +2673,8 @@ document.addEventListener('submit', async event => {
|
|
|
1991
2673
|
}
|
|
1992
2674
|
|
|
1993
2675
|
event.preventDefault();
|
|
1994
|
-
const
|
|
2676
|
+
const submitter = event.submitter instanceof HTMLElement ? event.submitter : null;
|
|
2677
|
+
const formData = submitter ? new FormData(form, submitter) : new FormData(form);
|
|
1995
2678
|
|
|
1996
2679
|
switch (form.dataset.form) {
|
|
1997
2680
|
case 'open-connection': {
|
|
@@ -2083,6 +2766,9 @@ document.addEventListener('submit', async event => {
|
|
|
2083
2766
|
case 'create-media-tagging-mapping-table':
|
|
2084
2767
|
await submitCreateMediaTaggingMappingTable();
|
|
2085
2768
|
return;
|
|
2769
|
+
case 'copy-column':
|
|
2770
|
+
await submitCopyColumnModal(formData);
|
|
2771
|
+
return;
|
|
2086
2772
|
case 'save-data-row': {
|
|
2087
2773
|
const values = {};
|
|
2088
2774
|
|