mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-26 06:26:00 +00:00
macro preview
This commit is contained in:
22
packages/datalib/src/MacroDefinition.ts
Normal file
22
packages/datalib/src/MacroDefinition.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export interface MacroArgument {
|
||||||
|
type: 'text' | 'select';
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MacroDefinition {
|
||||||
|
title: string;
|
||||||
|
name: string;
|
||||||
|
group: string;
|
||||||
|
description?: string;
|
||||||
|
type: 'transformValue';
|
||||||
|
code: string;
|
||||||
|
args?: MacroArgument[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MacroSelectedCell {
|
||||||
|
column: string;
|
||||||
|
row: number;
|
||||||
|
}
|
||||||
@@ -7,3 +7,5 @@ export * from "./ChangeSet";
|
|||||||
export * from "./filterName";
|
export * from "./filterName";
|
||||||
export * from "./FreeTableGridDisplay";
|
export * from "./FreeTableGridDisplay";
|
||||||
export * from "./FreeTableModel";
|
export * from "./FreeTableModel";
|
||||||
|
export * from "./MacroDefinition";
|
||||||
|
export * from "./runMacro";
|
||||||
|
|||||||
59
packages/datalib/src/runMacro.ts
Normal file
59
packages/datalib/src/runMacro.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { FreeTableModel } from './FreeTableModel';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { MacroDefinition, MacroSelectedCell } from './MacroDefinition';
|
||||||
|
|
||||||
|
const getMacroFunction = {
|
||||||
|
transformValue: (code) => `
|
||||||
|
(value, args, modules, rowIndex, row, columnName) => {
|
||||||
|
${code}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const modules = {
|
||||||
|
lodash: _,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function runMacro(
|
||||||
|
macro: MacroDefinition,
|
||||||
|
macroArgs: {},
|
||||||
|
data: FreeTableModel,
|
||||||
|
preview: boolean,
|
||||||
|
selectedCells: MacroSelectedCell[]
|
||||||
|
): FreeTableModel {
|
||||||
|
const func = eval(getMacroFunction[macro.type](macro.code));
|
||||||
|
if (macro.type == 'transformValue') {
|
||||||
|
const selectedRows = _.groupBy(selectedCells, 'row');
|
||||||
|
const rows = data.rows.map((row, rowIndex) => {
|
||||||
|
const selectedRow = selectedRows[rowIndex];
|
||||||
|
if (selectedRow) {
|
||||||
|
const columnSet = new Set(selectedRow.map((item) => item.column));
|
||||||
|
const changedValues = [];
|
||||||
|
const res = _.mapValues(row, (value, key) => {
|
||||||
|
if (columnSet.has(key)) {
|
||||||
|
const newValue = func(value, macroArgs, modules, rowIndex, row, key);
|
||||||
|
if (preview && newValue != value) changedValues.push(key);
|
||||||
|
return newValue;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (changedValues.length > 0) {
|
||||||
|
return {
|
||||||
|
...res,
|
||||||
|
__changedValues: new Set(changedValues),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
structure: data.structure,
|
||||||
|
rows,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
@@ -109,6 +109,7 @@ export default function DataGridCore(props) {
|
|||||||
onSave,
|
onSave,
|
||||||
isLoading,
|
isLoading,
|
||||||
grider,
|
grider,
|
||||||
|
onSelectionChanged,
|
||||||
} = props;
|
} = props;
|
||||||
// console.log('RENDER GRID', display.baseTable.pureName);
|
// console.log('RENDER GRID', display.baseTable.pureName);
|
||||||
const columns = React.useMemo(() => display.allColumns, [display]);
|
const columns = React.useMemo(() => display.allColumns, [display]);
|
||||||
@@ -205,6 +206,12 @@ export default function DataGridCore(props) {
|
|||||||
}
|
}
|
||||||
}, [tabVisible, focusFieldRef.current]);
|
}, [tabVisible, focusFieldRef.current]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (onSelectionChanged) {
|
||||||
|
onSelectionChanged(getSelectedMacroCells());
|
||||||
|
}
|
||||||
|
}, [onSelectionChanged, selectedCells]);
|
||||||
|
|
||||||
const maxScrollColumn = React.useMemo(() => {
|
const maxScrollColumn = React.useMemo(() => {
|
||||||
let newColumn = columnSizes.scrollInView(0, columns.length - 1 - columnSizes.frozenCount, gridScrollAreaWidth);
|
let newColumn = columnSizes.scrollInView(0, columns.length - 1 - columnSizes.frozenCount, gridScrollAreaWidth);
|
||||||
return newColumn;
|
return newColumn;
|
||||||
@@ -530,6 +537,32 @@ export default function DataGridCore(props) {
|
|||||||
return _.uniq((selectedCells || []).map((x) => x[1])).filter((x) => _.isNumber(x));
|
return _.uniq((selectedCells || []).map((x) => x[1])).filter((x) => _.isNumber(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSelectedRegularCells() {
|
||||||
|
if (selectedCells.find((x) => x[0] == 'header' && x[1] == 'header')) {
|
||||||
|
const row = _.range(0, realColumnUniqueNames.length);
|
||||||
|
return _.range(0, grider.rowCount).map((rowIndex) => row.map((colIndex) => [rowIndex, colIndex]));
|
||||||
|
}
|
||||||
|
const res = [];
|
||||||
|
for (const cell of selectedCells) {
|
||||||
|
if (isRegularCell(cell)) res.push(cell);
|
||||||
|
else if (cell[0] == 'header' && _.isNumber(cell[1])) {
|
||||||
|
res.push(..._.range(0, grider.rowCount).map((rowIndex) => [rowIndex, cell[1]]));
|
||||||
|
} else if (cell[1] == 'header' && _.isNumber(cell[0])) {
|
||||||
|
res.push(..._.range(0, realColumnUniqueNames.length).map((colIndex) => [cell[0], colIndex]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedMacroCells() {
|
||||||
|
const regular = getSelectedRegularCells();
|
||||||
|
// @ts-ignore
|
||||||
|
return regular.map((cell) => ({
|
||||||
|
row: cell[0],
|
||||||
|
column: realColumnUniqueNames[cell[1]],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
function getSelectedRowData() {
|
function getSelectedRowData() {
|
||||||
return _.compact(getSelectedRowIndexes().map((index) => grider.getRowData(index)));
|
return _.compact(getSelectedRowIndexes().map((index) => grider.getRowData(index)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,36 @@ import { createGridCache, FreeTableGridDisplay } from '@dbgate/datalib';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import DataGridCore from '../datagrid/DataGridCore';
|
import DataGridCore from '../datagrid/DataGridCore';
|
||||||
import FreeTableGrider from './FreeTableGrider';
|
import FreeTableGrider from './FreeTableGrider';
|
||||||
|
import MacroPreviewGrider from './MacroPreviewGrider';
|
||||||
|
|
||||||
export default function FreeTableGridCore(props) {
|
export default function FreeTableGridCore(props) {
|
||||||
const { modelState, dispatchModel, config, setConfig, macroPreview, macroValues } = props;
|
const { modelState, dispatchModel, config, setConfig, macroPreview, macroValues } = props;
|
||||||
const grider = React.useMemo(() => FreeTableGrider.factory(props), FreeTableGrider.factoryDeps(props));
|
|
||||||
const [cache, setCache] = React.useState(createGridCache());
|
const [cache, setCache] = React.useState(createGridCache());
|
||||||
|
const [selectedCells, setSelectedCells] = React.useState([]);
|
||||||
|
const grider = React.useMemo(
|
||||||
|
() =>
|
||||||
|
macroPreview
|
||||||
|
? new MacroPreviewGrider(modelState.value, macroPreview, macroValues, selectedCells)
|
||||||
|
: FreeTableGrider.factory(props),
|
||||||
|
[
|
||||||
|
...FreeTableGrider.factoryDeps(props),
|
||||||
|
macroPreview,
|
||||||
|
macroPreview ? macroValues : null,
|
||||||
|
macroPreview ? selectedCells : null,
|
||||||
|
]
|
||||||
|
);
|
||||||
const display = React.useMemo(() => new FreeTableGridDisplay(modelState.value, config, setConfig, cache, setCache), [
|
const display = React.useMemo(() => new FreeTableGridDisplay(modelState.value, config, setConfig, cache, setCache), [
|
||||||
modelState.value,
|
modelState.value,
|
||||||
config,
|
config,
|
||||||
cache,
|
cache,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return <DataGridCore {...props} grider={grider} display={display} />;
|
return (
|
||||||
|
<DataGridCore
|
||||||
|
{...props}
|
||||||
|
grider={grider}
|
||||||
|
display={display}
|
||||||
|
onSelectionChanged={macroPreview ? setSelectedCells : null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,28 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormTextField, FormSubmit, FormArchiveFolderSelect, FormRow, FormLabel } from '../utility/forms';
|
import _ from 'lodash';
|
||||||
|
import {
|
||||||
|
FormTextField,
|
||||||
|
FormSubmit,
|
||||||
|
FormArchiveFolderSelect,
|
||||||
|
FormRow,
|
||||||
|
FormLabel,
|
||||||
|
FormSelectField,
|
||||||
|
} from '../utility/forms';
|
||||||
import { Formik, Form, useFormikContext } from 'formik';
|
import { Formik, Form, useFormikContext } from 'formik';
|
||||||
|
|
||||||
function MacroArgument({ arg }) {
|
function MacroArgument({ arg }) {
|
||||||
if (arg.type == 'text') {
|
if (arg.type == 'text') {
|
||||||
return <FormTextField label={arg.label} name={arg.name} />;
|
return <FormTextField label={arg.label} name={arg.name} />;
|
||||||
}
|
}
|
||||||
|
if (arg.type == 'select') {
|
||||||
|
return (
|
||||||
|
<FormSelectField label={arg.label} name={arg.name}>
|
||||||
|
{arg.options.map((opt) =>
|
||||||
|
_.isString(opt) ? <option value={opt}>{opt}</option> : <option value={opt.value}>{opt.name}</option>
|
||||||
|
)}
|
||||||
|
</FormSelectField>
|
||||||
|
);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
packages/web/src/freetable/MacroPreviewGrider.ts
Normal file
17
packages/web/src/freetable/MacroPreviewGrider.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { FreeTableModel, MacroDefinition, MacroSelectedCell, runMacro } from '@dbgate/datalib';
|
||||||
|
import Grider from '../datagrid/Grider';
|
||||||
|
|
||||||
|
export default class MacroPreviewGrider extends Grider {
|
||||||
|
model: FreeTableModel;
|
||||||
|
constructor(model: FreeTableModel, macro: MacroDefinition, macroArgs: {}, selectedCells: MacroSelectedCell[]) {
|
||||||
|
super();
|
||||||
|
this.model = runMacro(macro, macroArgs, model, true, selectedCells);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowData(index: any) {
|
||||||
|
return this.model.rows[index];
|
||||||
|
}
|
||||||
|
get rowCount() {
|
||||||
|
return this.model.rows.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,15 @@ const macros = [
|
|||||||
{
|
{
|
||||||
title: 'Remove diacritics',
|
title: 'Remove diacritics',
|
||||||
name: 'removeDiacritics',
|
name: 'removeDiacritics',
|
||||||
group: 'text',
|
group: 'Text',
|
||||||
description: 'Removes diacritics from selected cells',
|
description: 'Removes diacritics from selected cells',
|
||||||
type: 'transformValue',
|
type: 'transformValue',
|
||||||
code: `value => modules.diacritics.remove(value)`,
|
code: `return modules.lodash.deburr(value)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Search & replace text',
|
title: 'Search & replace text',
|
||||||
name: 'stringReplace',
|
name: 'stringReplace',
|
||||||
group: 'text',
|
group: 'Text',
|
||||||
description: 'Search & replace text or regular expression',
|
description: 'Search & replace text or regular expression',
|
||||||
type: 'transformValue',
|
type: 'transformValue',
|
||||||
args: [
|
args: [
|
||||||
@@ -25,7 +25,31 @@ const macros = [
|
|||||||
name: 'replace',
|
name: 'replace',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
code: `value => value ? value.toString().replace(args.find, args.replace) : value`,
|
code: `return value ? value.toString().replace(args.find, args.replace) : value`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Change text case',
|
||||||
|
name: 'changeTextCase',
|
||||||
|
group: 'Text',
|
||||||
|
description: 'Uppercase, lowercase and other case functions',
|
||||||
|
type: 'transformValue',
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
options: ['toUpper', 'toLower', 'lowerCase', 'upperCase', 'kebabCase', 'snakeCase', 'camelCase', 'startCase'],
|
||||||
|
label: 'Type',
|
||||||
|
name: 'caseTransform',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
code: `return modules.lodash[args.caseTransform || 'toUpper'](value)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Row index',
|
||||||
|
name: 'rowIndex',
|
||||||
|
group: 'Tools',
|
||||||
|
description: 'index of row from 1 (autoincrement)',
|
||||||
|
type: 'transformValue',
|
||||||
|
code: `return rowIndex + 1`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user