mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
removed free table (data sheet) concept
This commit is contained in:
parent
7c4a47c4c6
commit
a77492440e
@ -3,7 +3,6 @@ const readline = require('readline');
|
||||
const path = require('path');
|
||||
const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('../utility/directories');
|
||||
const socket = require('../utility/socket');
|
||||
const { saveFreeTableData } = require('../utility/freeTableStorage');
|
||||
const loadFilesRecursive = require('../utility/loadFilesRecursive');
|
||||
const getJslFileName = require('../utility/getJslFileName');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
@ -162,34 +161,6 @@ module.exports = {
|
||||
return true;
|
||||
},
|
||||
|
||||
saveFreeTable_meta: true,
|
||||
async saveFreeTable({ folder, file, data }) {
|
||||
await saveFreeTableData(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), data);
|
||||
socket.emitChanged(`archive-files-changed`, { folder });
|
||||
return true;
|
||||
},
|
||||
|
||||
loadFreeTable_meta: true,
|
||||
async loadFreeTable({ folder, file }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fileStream = fs.createReadStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
|
||||
const liner = readline.createInterface({
|
||||
input: fileStream,
|
||||
});
|
||||
let structure = null;
|
||||
const rows = [];
|
||||
liner.on('line', line => {
|
||||
const data = JSON.parse(line);
|
||||
if (structure) rows.push(data);
|
||||
else structure = data;
|
||||
});
|
||||
liner.on('close', () => {
|
||||
resolve({ structure, rows });
|
||||
fileStream.close();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
saveText_meta: true,
|
||||
async saveText({ folder, file, text }) {
|
||||
await fs.writeFile(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), text);
|
||||
@ -206,6 +177,17 @@ module.exports = {
|
||||
return true;
|
||||
},
|
||||
|
||||
saveRows_meta: true,
|
||||
async saveRows({ folder, file, rows }) {
|
||||
const fileStream = fs.createWriteStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
|
||||
for (const row of rows) {
|
||||
await fileStream.write(JSON.stringify(row) + '\n');
|
||||
}
|
||||
await fileStream.close();
|
||||
socket.emitChanged(`archive-files-changed`, { folder });
|
||||
return true;
|
||||
},
|
||||
|
||||
async getNewArchiveFolder({ database }) {
|
||||
const isLink = database.endsWith(database);
|
||||
const name = isLink ? database.slice(0, -5) : database;
|
||||
|
@ -4,7 +4,6 @@ const lineReader = require('line-reader');
|
||||
const _ = require('lodash');
|
||||
const { __ } = require('lodash/fp');
|
||||
const DatastoreProxy = require('../utility/DatastoreProxy');
|
||||
const { saveFreeTableData } = require('../utility/freeTableStorage');
|
||||
const getJslFileName = require('../utility/getJslFileName');
|
||||
const JsonLinesDatastore = require('../utility/JsonLinesDatastore');
|
||||
const requirePluginFunction = require('../utility/requirePluginFunction');
|
||||
@ -189,18 +188,22 @@ module.exports = {
|
||||
// }
|
||||
},
|
||||
|
||||
saveFreeTable_meta: true,
|
||||
async saveFreeTable({ jslid, data }) {
|
||||
saveFreeTableData(getJslFileName(jslid), data);
|
||||
return true;
|
||||
},
|
||||
|
||||
saveText_meta: true,
|
||||
async saveText({ jslid, text }) {
|
||||
await fs.promises.writeFile(getJslFileName(jslid), text);
|
||||
return true;
|
||||
},
|
||||
|
||||
saveRows_meta: true,
|
||||
async saveRows({ jslid, rows }) {
|
||||
const fileStream = fs.createWriteStream(getJslFileName(jslid));
|
||||
for (const row of rows) {
|
||||
await fileStream.write(JSON.stringify(row) + '\n');
|
||||
}
|
||||
await fileStream.close();
|
||||
return true;
|
||||
},
|
||||
|
||||
extractTimelineChart_meta: true,
|
||||
async extractTimelineChart({ jslid, timestampFunction, aggregateFunction, measures }) {
|
||||
const timestamp = requirePluginFunction(timestampFunction);
|
||||
|
@ -1,15 +0,0 @@
|
||||
const fs = require('fs-extra');
|
||||
|
||||
async function saveFreeTableData(file, data) {
|
||||
const { structure, rows } = data;
|
||||
const fileStream = fs.createWriteStream(file);
|
||||
await fileStream.write(JSON.stringify({ __isStreamHeader: true, ...structure }) + '\n');
|
||||
for (const row of rows) {
|
||||
await fileStream.write(JSON.stringify(row) + '\n');
|
||||
}
|
||||
await fileStream.close();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
saveFreeTableData,
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
import _ from 'lodash';
|
||||
import type { EngineDriver, ViewInfo, ColumnInfo } from 'dbgate-types';
|
||||
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
|
||||
import { GridConfig, GridCache } from './GridConfig';
|
||||
import { FreeTableModel } from './FreeTableModel';
|
||||
import { analyseCollectionDisplayColumns } from '.';
|
||||
|
||||
export class FreeTableGridDisplay extends GridDisplay {
|
||||
constructor(
|
||||
public model: FreeTableModel,
|
||||
config: GridConfig,
|
||||
setConfig: ChangeConfigFunc,
|
||||
cache: GridCache,
|
||||
setCache: ChangeCacheFunc
|
||||
) {
|
||||
super(config, setConfig, cache, setCache);
|
||||
this.columns = model?.structure?.__isDynamicStructure
|
||||
? analyseCollectionDisplayColumns(model?.rows, this)
|
||||
: this.getDisplayColumns(model);
|
||||
this.filterable = false;
|
||||
this.sortable = false;
|
||||
}
|
||||
|
||||
getDisplayColumns(model: FreeTableModel) {
|
||||
return _.uniqBy(
|
||||
model?.structure?.columns
|
||||
?.map(col => this.getDisplayColumn(col))
|
||||
?.map(col => ({
|
||||
...col,
|
||||
isChecked: this.isColumnChecked(col),
|
||||
})) || [],
|
||||
col => col.uniqueName
|
||||
);
|
||||
}
|
||||
|
||||
getDisplayColumn(col: ColumnInfo) {
|
||||
const uniquePath = [col.columnName];
|
||||
const uniqueName = uniquePath.join('.');
|
||||
return {
|
||||
...col,
|
||||
pureName: 'data',
|
||||
schemaName: '',
|
||||
headerText: col.columnName,
|
||||
uniqueName,
|
||||
uniquePath,
|
||||
};
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import type { TableInfo } from 'dbgate-types';
|
||||
|
||||
export interface FreeTableModel {
|
||||
structure: TableInfo;
|
||||
rows: any[];
|
||||
}
|
||||
|
||||
export function createFreeTableModel() {
|
||||
return {
|
||||
structure: {
|
||||
columns: [
|
||||
{
|
||||
columnName: 'col1',
|
||||
},
|
||||
],
|
||||
foreignKeys: [],
|
||||
},
|
||||
rows: [
|
||||
{
|
||||
col1: 'val1',
|
||||
},
|
||||
{
|
||||
col1: 'val2',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import { FreeTableModel } from './FreeTableModel';
|
||||
import _ from 'lodash';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
@ -27,160 +26,6 @@ const modules = {
|
||||
moment,
|
||||
};
|
||||
|
||||
// function runTramsformValue(
|
||||
// func,
|
||||
// macroArgs: {},
|
||||
// data: FreeTableModel,
|
||||
// preview: boolean,
|
||||
// selectedCells: MacroSelectedCell[],
|
||||
// errors: string[] = []
|
||||
// ) {
|
||||
// const selectedRows = _.groupBy(selectedCells, 'row');
|
||||
// const rows = data.rows.map((row, rowIndex) => {
|
||||
// const selectedRow = selectedRows[rowIndex];
|
||||
// if (selectedRow) {
|
||||
// const modifiedFields = [];
|
||||
// let res = null;
|
||||
// for (const cell of selectedRow) {
|
||||
// const { column } = cell;
|
||||
// const oldValue = row[column];
|
||||
// let newValue = oldValue;
|
||||
// try {
|
||||
// newValue = func(oldValue, macroArgs, modules, rowIndex, row, column);
|
||||
// } catch (err) {
|
||||
// errors.push(`Error processing column ${column} on row ${rowIndex}: ${err.message}`);
|
||||
// }
|
||||
// if (newValue != oldValue) {
|
||||
// if (res == null) {
|
||||
// res = { ...row };
|
||||
// }
|
||||
// res[column] = newValue;
|
||||
// if (preview) modifiedFields.push(column);
|
||||
// }
|
||||
// }
|
||||
// if (res) {
|
||||
// if (modifiedFields.length > 0) {
|
||||
// return {
|
||||
// ...res,
|
||||
// __modifiedFields: new Set(modifiedFields),
|
||||
// };
|
||||
// }
|
||||
// return res;
|
||||
// }
|
||||
// return row;
|
||||
// } else {
|
||||
// return row;
|
||||
// }
|
||||
// });
|
||||
|
||||
// return {
|
||||
// structure: data.structure,
|
||||
// rows,
|
||||
// };
|
||||
// }
|
||||
|
||||
// function removePreviewRowFlags(rows) {
|
||||
// rows = rows.filter(row => row.__rowStatus != 'deleted');
|
||||
// rows = rows.map(row => {
|
||||
// if (row.__rowStatus || row.__modifiedFields || row.__insertedFields || row.__deletedFields)
|
||||
// return _.omit(row, ['__rowStatus', '__modifiedFields', '__insertedFields', '__deletedFields']);
|
||||
// return row;
|
||||
// });
|
||||
// return rows;
|
||||
// }
|
||||
|
||||
// function runTramsformRows(
|
||||
// func,
|
||||
// macroArgs: {},
|
||||
// data: FreeTableModel,
|
||||
// preview: boolean,
|
||||
// selectedCells: MacroSelectedCell[],
|
||||
// errors: string[] = []
|
||||
// ) {
|
||||
// let rows = data.rows;
|
||||
// try {
|
||||
// rows = func(
|
||||
// data.rows,
|
||||
// macroArgs,
|
||||
// modules,
|
||||
// selectedCells,
|
||||
// data.structure.columns.map(x => x.columnName),
|
||||
// data.structure.columns
|
||||
// );
|
||||
// if (!preview) {
|
||||
// rows = removePreviewRowFlags(rows);
|
||||
// }
|
||||
// } catch (err) {
|
||||
// errors.push(`Error processing rows: ${err.message}`);
|
||||
// }
|
||||
// return {
|
||||
// structure: data.structure,
|
||||
// rows,
|
||||
// };
|
||||
// }
|
||||
|
||||
// function runTramsformData(
|
||||
// func,
|
||||
// macroArgs: {},
|
||||
// data: FreeTableModel,
|
||||
// preview: boolean,
|
||||
// selectedCells: MacroSelectedCell[],
|
||||
// errors: string[] = []
|
||||
// ) {
|
||||
// try {
|
||||
// let { rows, columns, cols } = func(
|
||||
// data.rows,
|
||||
// macroArgs,
|
||||
// modules,
|
||||
// selectedCells,
|
||||
// data.structure.columns.map(x => x.columnName),
|
||||
// data.structure.columns
|
||||
// );
|
||||
// if (cols && !columns) {
|
||||
// columns = cols.map(columnName => ({ columnName }));
|
||||
// }
|
||||
// columns = _.uniqBy(columns, 'columnName');
|
||||
// if (!preview) {
|
||||
// rows = removePreviewRowFlags(rows);
|
||||
// }
|
||||
// return {
|
||||
// structure: { columns },
|
||||
// rows,
|
||||
// };
|
||||
// } catch (err) {
|
||||
// errors.push(`Error processing data: ${err.message}`);
|
||||
// }
|
||||
// return data;
|
||||
// }
|
||||
|
||||
// export function runMacro(
|
||||
// macro: MacroDefinition,
|
||||
// macroArgs: {},
|
||||
// data: FreeTableModel,
|
||||
// preview: boolean,
|
||||
// selectedCells: MacroSelectedCell[],
|
||||
// errors: string[] = []
|
||||
// ): FreeTableModel {
|
||||
// let func;
|
||||
// try {
|
||||
// func = eval(getMacroFunction[macro.type](macro.code));
|
||||
// } catch (err) {
|
||||
// errors.push(`Error compiling macro ${macro.name}: ${err.message}`);
|
||||
// return data;
|
||||
// }
|
||||
// if (macro.type == 'transformValue') {
|
||||
// return runTramsformValue(func, macroArgs, data, preview, selectedCells, errors);
|
||||
// }
|
||||
// if (macro.type == 'transformRows') {
|
||||
// return runTramsformRows(func, macroArgs, data, preview, selectedCells, errors);
|
||||
// }
|
||||
// if (macro.type == 'transformData') {
|
||||
// // @ts-ignore
|
||||
// return runTramsformData(func, macroArgs, data, preview, selectedCells, errors);
|
||||
// }
|
||||
// return data;
|
||||
// }
|
||||
|
||||
export function compileMacroFunction(macro: MacroDefinition, errors = []) {
|
||||
if (!macro) return null;
|
||||
let func;
|
||||
|
@ -111,24 +111,6 @@
|
||||
const handleOpenArchive = () => {
|
||||
openArchive(data.fileName, data.folderName);
|
||||
};
|
||||
const handleOpenDataSheet = () => {
|
||||
openNewTab({
|
||||
title: data.fileName,
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {
|
||||
initialArgs: {
|
||||
functionName: 'archiveReader',
|
||||
props: {
|
||||
fileName: data.fileName,
|
||||
folderName: data.folderName,
|
||||
},
|
||||
},
|
||||
archiveFile: data.fileName,
|
||||
archiveFolder: data.folderName,
|
||||
},
|
||||
});
|
||||
};
|
||||
const handleClick = () => {
|
||||
if (data.fileType == 'jsonl') {
|
||||
handleOpenArchive();
|
||||
@ -153,7 +135,6 @@
|
||||
function createMenu() {
|
||||
return [
|
||||
data.fileType == 'jsonl' && { text: 'Open', onClick: handleOpenArchive },
|
||||
data.fileType == 'jsonl' && { text: 'Open as data sheet', onClick: handleOpenDataSheet },
|
||||
data.fileType == 'jsonl' && { text: 'Open in text editor', onClick: handleOpenJsonLinesText },
|
||||
{ text: 'Delete', onClick: handleDelete },
|
||||
{ text: 'Rename', onClick: handleRename },
|
||||
|
@ -102,10 +102,6 @@
|
||||
isImport: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
{
|
||||
label: 'Open as data sheet',
|
||||
isOpenFreeTable: true,
|
||||
},
|
||||
{
|
||||
label: 'Open active chart',
|
||||
isActiveChart: true,
|
||||
@ -176,10 +172,6 @@
|
||||
isExport: true,
|
||||
functionName: 'tableReader',
|
||||
},
|
||||
{
|
||||
label: 'Open as data sheet',
|
||||
isOpenFreeTable: true,
|
||||
},
|
||||
{
|
||||
label: 'Open active chart',
|
||||
isActiveChart: true,
|
||||
@ -242,10 +234,6 @@
|
||||
isExport: true,
|
||||
functionName: 'tableReader',
|
||||
},
|
||||
{
|
||||
label: 'Open as data sheet',
|
||||
isOpenFreeTable: true,
|
||||
},
|
||||
{
|
||||
label: 'Open active chart',
|
||||
isActiveChart: true,
|
||||
@ -409,27 +397,7 @@
|
||||
return driver;
|
||||
};
|
||||
|
||||
if (menu.isOpenFreeTable) {
|
||||
const coninfo = await getConnectionInfo(data);
|
||||
openNewTab({
|
||||
title: data.pureName,
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {
|
||||
initialArgs: {
|
||||
functionName: 'tableReader',
|
||||
props: {
|
||||
connection: {
|
||||
...coninfo,
|
||||
database: data.database,
|
||||
},
|
||||
schemaName: data.schemaName,
|
||||
pureName: data.pureName,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} else if (menu.isActiveChart) {
|
||||
if (menu.isActiveChart) {
|
||||
const driver = await getDriver();
|
||||
const dmp = driver.createDumper();
|
||||
dmp.put('^select * from %f', data);
|
||||
|
@ -329,21 +329,6 @@ registerCommand({
|
||||
},
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'new.freetable',
|
||||
category: 'New',
|
||||
icon: 'img markdown',
|
||||
name: 'Data sheet',
|
||||
menuName: 'New data sheet',
|
||||
onClick: () => {
|
||||
openNewTab({
|
||||
title: 'Data #',
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'new.jsonl',
|
||||
category: 'New',
|
||||
|
@ -63,7 +63,6 @@
|
||||
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
||||
import ColumnManager from './ColumnManager.svelte';
|
||||
import ReferenceManager from './ReferenceManager.svelte';
|
||||
import FreeTableColumnEditor from '../freetable/FreeTableColumnEditor.svelte';
|
||||
import JsonViewFilters from '../jsonview/JsonViewFilters.svelte';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import _ from 'lodash';
|
||||
@ -201,7 +200,6 @@
|
||||
height="40%"
|
||||
show={freeTableColumn && !isDynamicStructure}
|
||||
>
|
||||
<FreeTableColumnEditor {...$$props} {managerSize} />
|
||||
</WidgetColumnBarItem>
|
||||
|
||||
<WidgetColumnBarItem title="Filters" name="filters" height="30%" show={isFormView}>
|
||||
|
@ -7,6 +7,7 @@
|
||||
import CellValue from './CellValue.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import EditCellDataModal from '../modals/EditCellDataModal.svelte';
|
||||
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
||||
|
||||
export let rowIndex;
|
||||
export let col;
|
||||
@ -96,20 +97,7 @@
|
||||
icon="icon open-in-new"
|
||||
on:click={() => {
|
||||
if (_.every(jsonParsedValue || value, x => _.isPlainObject(x))) {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Data #',
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
editor: {
|
||||
rows: jsonParsedValue || value,
|
||||
structure: { __isDynamicStructure: true, columns: [] },
|
||||
},
|
||||
}
|
||||
);
|
||||
openJsonLinesData(jsonParsedValue || value);
|
||||
} else {
|
||||
openJsonDocument(jsonParsedValue || value, undefined, true);
|
||||
}
|
||||
|
@ -161,7 +161,7 @@
|
||||
registerCommand({
|
||||
id: 'dataGrid.openJsonArrayInSheet',
|
||||
category: 'Data grid',
|
||||
name: 'Open array as data sheet',
|
||||
name: 'Open array as table',
|
||||
testEnabled: () => getCurrentDataGrid()?.openJsonArrayInSheetEnabled(),
|
||||
onClick: () => getCurrentDataGrid().openJsonArrayInSheet(),
|
||||
});
|
||||
@ -238,7 +238,7 @@
|
||||
registerCommand({
|
||||
id: 'dataGrid.openFreeTable',
|
||||
category: 'Data grid',
|
||||
name: 'Edit selection as data sheet',
|
||||
name: 'Edit selection as table',
|
||||
testEnabled: () => getCurrentDataGrid() != null,
|
||||
onClick: () => getCurrentDataGrid().openFreeTable(),
|
||||
});
|
||||
@ -398,6 +398,7 @@
|
||||
import EditCellDataModal, { shouldOpenMultilineDialog } from '../modals/EditCellDataModal.svelte';
|
||||
import { getDatabaseInfo, useDatabaseStatus } from '../utility/metadataLoaders';
|
||||
import { showSnackbarSuccess } from '../utility/snackbar';
|
||||
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
||||
|
||||
export let onLoadNextData = undefined;
|
||||
export let grider = undefined;
|
||||
@ -645,15 +646,7 @@
|
||||
}
|
||||
|
||||
export function openFreeTable() {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Data #',
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {},
|
||||
},
|
||||
{ editor: getSelectedFreeData() }
|
||||
);
|
||||
openJsonLinesData(getSelectedFreeDataRows());
|
||||
}
|
||||
|
||||
export function openChartFromSelection() {
|
||||
@ -784,20 +777,7 @@
|
||||
}
|
||||
|
||||
export function openJsonArrayInSheet() {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Data #',
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
editor: {
|
||||
rows: getSelectedDataJson(true),
|
||||
structure: { __isDynamicStructure: true, columns: [] },
|
||||
},
|
||||
}
|
||||
);
|
||||
openJsonLinesData(getSelectedDataJson(true));
|
||||
}
|
||||
|
||||
export function editJsonEnabled() {
|
||||
@ -1109,6 +1089,12 @@
|
||||
};
|
||||
};
|
||||
|
||||
const getSelectedFreeDataRows = () => {
|
||||
const columns = getSelectedColumns();
|
||||
const rows = getSelectedRowData().map(row => _.pickBy(row, (v, col) => columns.find(x => x.columnName == col)));
|
||||
return rows;
|
||||
};
|
||||
|
||||
function getCellsPublished(cells) {
|
||||
const regular = cellsToRegularCells(cells);
|
||||
const res = regular
|
||||
|
@ -1,82 +0,0 @@
|
||||
<script context="module">
|
||||
function dispatchChangeColumns(props, func, rowFunc = null) {
|
||||
const { modelState, dispatchModel } = props;
|
||||
const model = modelState.value;
|
||||
|
||||
dispatchModel({
|
||||
type: 'set',
|
||||
value: {
|
||||
rows: rowFunc ? model.rows.map(rowFunc) : model.rows,
|
||||
structure: {
|
||||
...model.structure,
|
||||
columns: func(model.structure?.columns),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function exchange(array, i1, i2) {
|
||||
const i1r = (i1 + array.length) % array.length;
|
||||
const i2r = (i2 + array.length) % array.length;
|
||||
const res = [...array];
|
||||
[res[i1r], res[i2r]] = [res[i2r], res[i1r]];
|
||||
return res;
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import ManagerInnerContainer from '../elements/ManagerInnerContainer.svelte';
|
||||
import ColumnManagerRow from './ColumnManagerRow.svelte';
|
||||
import ColumnNameEditor from './ColumnNameEditor.svelte';
|
||||
|
||||
export let modelState;
|
||||
export let dispatchModel;
|
||||
export let managerSize;
|
||||
|
||||
let editingColumn = null;
|
||||
|
||||
$: structure = modelState.value.structure;
|
||||
</script>
|
||||
|
||||
<ManagerInnerContainer width={managerSize}>
|
||||
{#each structure?.columns || [] as column, index}
|
||||
{#if index == editingColumn}
|
||||
<ColumnNameEditor
|
||||
defaultValue={column.columnName}
|
||||
onEnter={columnName => {
|
||||
dispatchChangeColumns(
|
||||
$$props,
|
||||
cols => cols.map((col, i) => (index == i ? { columnName } : col)),
|
||||
row => _.mapKeys(row, (v, k) => (k == column.columnName ? columnName : k))
|
||||
);
|
||||
}}
|
||||
onBlur={() => (editingColumn = null)}
|
||||
focusOnCreate
|
||||
blurOnEnter
|
||||
existingNames={structure?.columns.map(x => x.columnName)}
|
||||
/>
|
||||
{:else}
|
||||
<ColumnManagerRow
|
||||
{column}
|
||||
onEdit={() => (editingColumn = index)}
|
||||
onRemove={() => {
|
||||
dispatchChangeColumns($$props, cols => cols.filter((c, i) => i != index));
|
||||
}}
|
||||
onUp={() => {
|
||||
dispatchChangeColumns($$props, cols => exchange(cols, index, index - 1));
|
||||
}}
|
||||
onDown={() => {
|
||||
dispatchChangeColumns($$props, cols => exchange(cols, index, index + 1));
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<ColumnNameEditor
|
||||
onEnter={columnName => {
|
||||
dispatchChangeColumns($$props, cols => [...cols, { columnName }]);
|
||||
}}
|
||||
placeholder="New column"
|
||||
existingNames={(structure?.columns || []).map(x => x.columnName)}
|
||||
/>
|
||||
</ManagerInnerContainer>
|
@ -1,84 +0,0 @@
|
||||
<script context="module" lang="ts">
|
||||
const getCurrentEditor = () => getActiveComponent('FreeTableGridCore');
|
||||
|
||||
registerCommand({
|
||||
id: 'freeTableGrid.export',
|
||||
category: 'Data grid',
|
||||
icon: 'icon export',
|
||||
name: 'Export',
|
||||
keyText: 'CtrlOrCommand+E',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().exportGrid(),
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { createGridCache, FreeTableGridDisplay } from 'dbgate-datalib';
|
||||
import { writable } from 'svelte/store';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
|
||||
import DataGridCore from '../datagrid/DataGridCore.svelte';
|
||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { registerMenu } from '../utility/contextMenu';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
||||
import { exportQuickExportFile } from '../utility/exportFileTools';
|
||||
import FreeTableGrider from './FreeTableGrider';
|
||||
import MacroPreviewGrider from './MacroPreviewGrider';
|
||||
|
||||
export let macroPreview;
|
||||
export let modelState;
|
||||
export let dispatchModel;
|
||||
export let macroValues;
|
||||
export let config;
|
||||
export let setConfig;
|
||||
export let selectedCellsPublished;
|
||||
|
||||
export const activator = createActivator('FreeTableGridCore', false);
|
||||
|
||||
const cache = writable(createGridCache());
|
||||
|
||||
$: grider = macroPreview
|
||||
? new MacroPreviewGrider(modelState.value, macroPreview, macroValues, selectedCellsPublished())
|
||||
: new FreeTableGrider(modelState, dispatchModel);
|
||||
$: display = new FreeTableGridDisplay(grider.model || modelState.value, config, setConfig, $cache, cache.update);
|
||||
|
||||
export async function exportGrid() {
|
||||
const jslid = uuidv1();
|
||||
await apiCall('jsldata/save-free-table', { jslid, data: modelState.value });
|
||||
const initialValues: any = {};
|
||||
initialValues.sourceStorageType = 'jsldata';
|
||||
initialValues.sourceJslId = jslid;
|
||||
initialValues.sourceList = ['editor-data'];
|
||||
initialValues[`columns_editor-data`] = display.getExportColumnMap();
|
||||
showModal(ImportExportModal, { initialValues: initialValues });
|
||||
}
|
||||
|
||||
const quickExportHandler = fmt => async () => {
|
||||
const jslid = uuidv1();
|
||||
await apiCall('jsldata/save-free-table', { jslid, data: modelState.value });
|
||||
exportQuickExportFile(
|
||||
'editor-data',
|
||||
{
|
||||
functionName: 'jslDataReader',
|
||||
props: {
|
||||
jslid,
|
||||
},
|
||||
},
|
||||
fmt,
|
||||
display.getExportColumnMap()
|
||||
);
|
||||
};
|
||||
registerQuickExportHandler(quickExportHandler);
|
||||
|
||||
registerMenu(() => ({
|
||||
...createQuickExportMenu(quickExportHandler, { command: 'freeTableGrid.export' }),
|
||||
tag: 'export',
|
||||
}));
|
||||
</script>
|
||||
|
||||
<DataGridCore {...$$props} {grider} {display} frameSelection={!!macroPreview} bind:selectedCellsPublished />
|
@ -1,104 +0,0 @@
|
||||
import type { FreeTableModel } from 'dbgate-datalib';
|
||||
import Grider from '../datagrid/Grider';
|
||||
|
||||
export default class FreeTableGrider extends Grider {
|
||||
public model: FreeTableModel;
|
||||
private batchModel: FreeTableModel;
|
||||
|
||||
constructor(public modelState, public dispatchModel) {
|
||||
super();
|
||||
this.model = modelState && modelState.value;
|
||||
}
|
||||
getRowData(index: any) {
|
||||
return this.model.rows?.[index];
|
||||
}
|
||||
get rowCount() {
|
||||
return this.model.rows?.length;
|
||||
}
|
||||
get currentModel(): FreeTableModel {
|
||||
return this.batchModel || this.model;
|
||||
}
|
||||
set currentModel(value) {
|
||||
if (this.batchModel) this.batchModel = value;
|
||||
else this.dispatchModel({ type: 'set', value });
|
||||
}
|
||||
setCellValue(index: number, uniqueName: string, value: any) {
|
||||
const model = this.currentModel;
|
||||
if (model.rows[index]) {
|
||||
this.currentModel = {
|
||||
...model,
|
||||
rows: model.rows.map((row, i) => (index == i ? { ...row, [uniqueName]: value } : row)),
|
||||
};
|
||||
}
|
||||
}
|
||||
setRowData(index: number, document: any) {
|
||||
const model = this.currentModel;
|
||||
if (model.rows[index]) {
|
||||
this.currentModel = {
|
||||
...model,
|
||||
rows: model.rows.map((row, i) => (index == i ? document : row)),
|
||||
};
|
||||
}
|
||||
}
|
||||
get editable() {
|
||||
return true;
|
||||
}
|
||||
get canInsert() {
|
||||
return true;
|
||||
}
|
||||
get allowSave() {
|
||||
return true;
|
||||
}
|
||||
insertRow(): number {
|
||||
const model = this.currentModel;
|
||||
this.currentModel = {
|
||||
...model,
|
||||
rows: [...model.rows, {}],
|
||||
};
|
||||
return this.currentModel.rows.length - 1;
|
||||
}
|
||||
insertDocuments(documents: any[]): number {
|
||||
const model = this.currentModel;
|
||||
this.currentModel = {
|
||||
...model,
|
||||
rows: [...model.rows, ...documents],
|
||||
};
|
||||
return this.currentModel.rows.length - documents.length;
|
||||
}
|
||||
|
||||
deleteRow(index: number) {
|
||||
const model = this.currentModel;
|
||||
this.currentModel = {
|
||||
...model,
|
||||
rows: model.rows.filter((row, i) => index != i),
|
||||
};
|
||||
}
|
||||
beginUpdate() {
|
||||
this.batchModel = this.model;
|
||||
}
|
||||
endUpdate() {
|
||||
if (this.model != this.batchModel) {
|
||||
this.dispatchModel({ type: 'set', value: this.batchModel });
|
||||
this.batchModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
// static factory({ modelState, dispatchModel }): FreeTableGrider {
|
||||
// return new FreeTableGrider(modelState, dispatchModel);
|
||||
// }
|
||||
// static factoryDeps({ modelState, dispatchModel }) {
|
||||
// return [modelState, dispatchModel];
|
||||
// }
|
||||
undo() {
|
||||
this.dispatchModel({ type: 'undo' });
|
||||
}
|
||||
redo() {
|
||||
this.dispatchModel({ type: 'redo' });
|
||||
}
|
||||
get canUndo() {
|
||||
return this.modelState.canUndo;
|
||||
}
|
||||
get canRedo() {
|
||||
return this.modelState.canRedo;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import _ from 'lodash';
|
||||
import { copyTextToClipboard } from '../utility/clipboard';
|
||||
import { openJsonLinesData } from '../utility/openJsonLinesData';
|
||||
|
||||
setContext('json-tree-context-key', {});
|
||||
|
||||
@ -49,22 +50,9 @@
|
||||
|
||||
if (value && _.isArray(value)) {
|
||||
res.push({
|
||||
text: 'Open as data sheet',
|
||||
text: 'Open as table',
|
||||
onClick: () => {
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Data #',
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
props: {},
|
||||
},
|
||||
{
|
||||
editor: {
|
||||
rows: value,
|
||||
structure: { __isDynamicStructure: true, columns: [] },
|
||||
},
|
||||
}
|
||||
);
|
||||
openJsonLinesData(value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -1,167 +0,0 @@
|
||||
<script lang="ts" context="module">
|
||||
const getCurrentEditor = () => getActiveComponent('FreeTableTab');
|
||||
|
||||
registerCommand({
|
||||
id: 'freeTable.save',
|
||||
group: 'save',
|
||||
category: 'Table data',
|
||||
name: 'Save',
|
||||
// keyText: 'CtrlOrCommand+S',
|
||||
toolbar: true,
|
||||
isRelatedToTab: true,
|
||||
icon: 'icon save',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().save(),
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'freeTable.toggleDynamicStructure',
|
||||
category: 'Table data',
|
||||
name: 'Toggle dynamic structure',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().toggleDynamicStructure(),
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
analyseCollectionDisplayColumns,
|
||||
createFreeTableModel,
|
||||
FreeTableGridDisplay,
|
||||
runMacro,
|
||||
} from 'dbgate-datalib';
|
||||
import { setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||
import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte';
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
import DataGrid from '../datagrid/DataGrid.svelte';
|
||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||
|
||||
import FreeTableGridCore from '../freetable/FreeTableGridCore.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import SaveArchiveModal from '../modals/SaveArchiveModal.svelte';
|
||||
import useEditorData from '../query/useEditorData';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { changeTab } from '../utility/common';
|
||||
import { registerMenu } from '../utility/contextMenu';
|
||||
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||
import createUndoReducer from '../utility/createUndoReducer';
|
||||
import { getLocalStorage, setLocalStorage } from '../utility/storageCache';
|
||||
import useGridConfig from '../utility/useGridConfig';
|
||||
|
||||
export let tabid;
|
||||
export let initialArgs;
|
||||
export let archiveFolder;
|
||||
export let archiveFile;
|
||||
|
||||
export const activator = createActivator('FreeTableTab', true);
|
||||
|
||||
const config = useGridConfig(tabid);
|
||||
const [modelState, dispatchModel] = createUndoReducer(createFreeTableModel());
|
||||
|
||||
const { setEditorData, editorState } = useEditorData({
|
||||
tabid,
|
||||
loadFromArgs: initialArgs && initialArgs.functionName ? () => apiCall('runners/load-reader', initialArgs) : null,
|
||||
onInitialData: value => {
|
||||
dispatchModel({ type: 'reset', value });
|
||||
},
|
||||
});
|
||||
|
||||
$: isLoading = $editorState.isLoading;
|
||||
$: errorMessage = $editorState.errorMessage;
|
||||
|
||||
$: setEditorData($modelState.value);
|
||||
|
||||
export function save() {
|
||||
showModal(SaveArchiveModal, {
|
||||
folder: archiveFolder,
|
||||
file: archiveFile,
|
||||
onSave: doSave,
|
||||
});
|
||||
}
|
||||
|
||||
const doSave = async (folder, file) => {
|
||||
await apiCall('archive/save-free-table', { folder, file, data: $modelState.value });
|
||||
changeTab(tabid, tab => ({
|
||||
...tab,
|
||||
title: file,
|
||||
props: { archiveFile: file, archiveFolder: folder },
|
||||
archiveFile: file,
|
||||
archiveFolder: folder,
|
||||
}));
|
||||
archiveFile = file;
|
||||
archiveFolder = folder;
|
||||
};
|
||||
|
||||
function handleRunMacro(macro, params, cells) {
|
||||
const newModel = runMacro(macro, params, $modelState.value, false, cells);
|
||||
dispatchModel({ type: 'set', value: newModel });
|
||||
}
|
||||
|
||||
const collapsedLeftColumnStore = writable(getLocalStorage('freeTable_collapsedLeftColumn', false));
|
||||
setContext('collapsedLeftColumnStore', collapsedLeftColumnStore);
|
||||
$: setLocalStorage('freeTable_collapsedLeftColumn', $collapsedLeftColumnStore);
|
||||
|
||||
export function toggleDynamicStructure() {
|
||||
let structure = $modelState.value.structure;
|
||||
structure = { ...structure, __isDynamicStructure: !structure.__isDynamicStructure };
|
||||
if (!structure.__isDynamicStructure) {
|
||||
const columns = analyseCollectionDisplayColumns($modelState.value.rows, display);
|
||||
structure = {
|
||||
...structure,
|
||||
columns: columns
|
||||
.filter(col => col.uniquePath.length == 1)
|
||||
.map(col => ({
|
||||
columnName: col.uniqueName,
|
||||
})),
|
||||
};
|
||||
}
|
||||
dispatchModel({
|
||||
type: 'set',
|
||||
value: {
|
||||
...$modelState.value,
|
||||
structure,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
registerMenu(
|
||||
{ command: 'freeTable.save', tag: 'save' },
|
||||
{ command: 'freeTable.toggleDynamicStructure', tag: 'export' }
|
||||
);
|
||||
|
||||
// display is overridden in FreeTableGridCore, this is because of column manager
|
||||
$: display = new FreeTableGridDisplay($modelState.value, $config, config.update, null, null);
|
||||
|
||||
const quickExportHandlerRef = createQuickExportHandlerRef();
|
||||
</script>
|
||||
|
||||
{#if isLoading}
|
||||
<LoadingInfo wrapper message="Loading data" />
|
||||
{:else if errorMessage}
|
||||
<ErrorInfo message={errorMessage} />
|
||||
{:else}
|
||||
<ToolStripContainer>
|
||||
<DataGrid
|
||||
config={$config}
|
||||
setConfig={config.update}
|
||||
modelState={$modelState}
|
||||
{dispatchModel}
|
||||
focusOnVisible
|
||||
gridCoreComponent={FreeTableGridCore}
|
||||
freeTableColumn
|
||||
showMacros
|
||||
expandMacros
|
||||
onRunMacro={handleRunMacro}
|
||||
isDynamicStructure={$modelState.value?.structure?.__isDynamicStructure}
|
||||
{display}
|
||||
/>
|
||||
<svelte:fragment slot="toolstrip">
|
||||
<ToolStripCommandButton command="freeTable.save" />
|
||||
<ToolStripExportButton command="freeTableGrid.export" {quickExportHandlerRef} />
|
||||
</svelte:fragment>
|
||||
</ToolStripContainer>
|
||||
{/if}
|
@ -5,7 +5,6 @@ import * as TableStructureTab from './TableStructureTab.svelte';
|
||||
import * as QueryTab from './QueryTab.svelte';
|
||||
import * as ShellTab from './ShellTab.svelte';
|
||||
import * as ArchiveFileTab from './ArchiveFileTab.svelte';
|
||||
import * as FreeTableTab from './FreeTableTab.svelte';
|
||||
import * as PluginTab from './PluginTab.svelte';
|
||||
import * as ChartTab from './ChartTab.svelte';
|
||||
import * as MarkdownEditorTab from './MarkdownEditorTab.svelte';
|
||||
@ -38,7 +37,6 @@ export default {
|
||||
QueryTab,
|
||||
ShellTab,
|
||||
ArchiveFileTab,
|
||||
FreeTableTab,
|
||||
PluginTab,
|
||||
ChartTab,
|
||||
MarkdownEditorTab,
|
||||
|
17
packages/web/src/utility/openJsonLinesData.ts
Normal file
17
packages/web/src/utility/openJsonLinesData.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import { apiCall } from './api';
|
||||
import openNewTab from './openNewTab';
|
||||
|
||||
export async function openJsonLinesData(rows) {
|
||||
const jslid = uuidv1();
|
||||
|
||||
await apiCall('jsldata/save-rows', { jslid, rows });
|
||||
openNewTab({
|
||||
tabComponent: 'ArchiveFileTab',
|
||||
icon: 'img archive',
|
||||
title: 'Data #',
|
||||
props: {
|
||||
jslid,
|
||||
},
|
||||
});
|
||||
}
|
@ -42,30 +42,26 @@
|
||||
apiCall('archive/refresh-files', { folder });
|
||||
};
|
||||
|
||||
function handleNewDataSheet() {
|
||||
function handleNewJsonLines() {
|
||||
showModal(InputTextModal, {
|
||||
value: '',
|
||||
label: 'New file name',
|
||||
header: 'Create new data sheet',
|
||||
header: 'Create new JSON lines',
|
||||
onConfirm: async file => {
|
||||
await apiCall('archive/save-free-table', {
|
||||
await apiCall('archive/save-rows', {
|
||||
folder: $currentArchive,
|
||||
file,
|
||||
data: createFreeTableModel(),
|
||||
rows: [
|
||||
{ id: 1, value: 'val1' },
|
||||
{ id: 1, value: 'val2' },
|
||||
],
|
||||
});
|
||||
|
||||
openNewTab({
|
||||
title: file,
|
||||
icon: 'img free-table',
|
||||
tabComponent: 'FreeTableTab',
|
||||
icon: 'img archive',
|
||||
tabComponent: 'ArchiveFileTab',
|
||||
props: {
|
||||
initialArgs: {
|
||||
functionName: 'archiveReader',
|
||||
props: {
|
||||
fileName: file,
|
||||
folderName: $currentArchive,
|
||||
},
|
||||
},
|
||||
archiveFile: file,
|
||||
archiveFolder: $currentArchive,
|
||||
},
|
||||
@ -75,7 +71,7 @@
|
||||
}
|
||||
|
||||
function createAddMenu() {
|
||||
return [{ text: 'New data sheet', onClick: handleNewDataSheet }];
|
||||
return [{ text: 'New NDJSON file', onClick: handleNewJsonLines }];
|
||||
}
|
||||
</script>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user