macro preview

This commit is contained in:
Jan Prochazka
2020-10-31 09:04:59 +01:00
parent d243e8cee5
commit cc385c12ec
8 changed files with 201 additions and 7 deletions

View 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;
}

View File

@@ -7,3 +7,5 @@ export * from "./ChangeSet";
export * from "./filterName";
export * from "./FreeTableGridDisplay";
export * from "./FreeTableModel";
export * from "./MacroDefinition";
export * from "./runMacro";

View 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;
}

View File

@@ -109,6 +109,7 @@ export default function DataGridCore(props) {
onSave,
isLoading,
grider,
onSelectionChanged,
} = props;
// console.log('RENDER GRID', display.baseTable.pureName);
const columns = React.useMemo(() => display.allColumns, [display]);
@@ -205,6 +206,12 @@ export default function DataGridCore(props) {
}
}, [tabVisible, focusFieldRef.current]);
React.useEffect(() => {
if (onSelectionChanged) {
onSelectionChanged(getSelectedMacroCells());
}
}, [onSelectionChanged, selectedCells]);
const maxScrollColumn = React.useMemo(() => {
let newColumn = columnSizes.scrollInView(0, columns.length - 1 - columnSizes.frozenCount, gridScrollAreaWidth);
return newColumn;
@@ -530,6 +537,32 @@ export default function DataGridCore(props) {
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() {
return _.compact(getSelectedRowIndexes().map((index) => grider.getRowData(index)));
}

View File

@@ -2,16 +2,36 @@ import { createGridCache, FreeTableGridDisplay } from '@dbgate/datalib';
import React from 'react';
import DataGridCore from '../datagrid/DataGridCore';
import FreeTableGrider from './FreeTableGrider';
import MacroPreviewGrider from './MacroPreviewGrider';
export default function FreeTableGridCore(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 [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), [
modelState.value,
config,
cache,
]);
return <DataGridCore {...props} grider={grider} display={display} />;
return (
<DataGridCore
{...props}
grider={grider}
display={display}
onSelectionChanged={macroPreview ? setSelectedCells : null}
/>
);
}

View File

@@ -1,11 +1,28 @@
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';
function MacroArgument({ arg }) {
if (arg.type == 'text') {
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;
}

View 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;
}
}

View File

@@ -2,15 +2,15 @@ const macros = [
{
title: 'Remove diacritics',
name: 'removeDiacritics',
group: 'text',
group: 'Text',
description: 'Removes diacritics from selected cells',
type: 'transformValue',
code: `value => modules.diacritics.remove(value)`,
code: `return modules.lodash.deburr(value)`,
},
{
title: 'Search & replace text',
name: 'stringReplace',
group: 'text',
group: 'Text',
description: 'Search & replace text or regular expression',
type: 'transformValue',
args: [
@@ -25,7 +25,31 @@ const macros = [
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`,
},
];