mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 10:06:00 +00:00
free table editor
This commit is contained in:
50
packages/web/src/freetable/ColumnManagerRow.svelte
Normal file
50
packages/web/src/freetable/ColumnManagerRow.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script lang="ts">
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
|
||||
export let column;
|
||||
export let onEdit;
|
||||
export let onRemove;
|
||||
export let onUp;
|
||||
export let onDown;
|
||||
</script>
|
||||
|
||||
<div class="row">
|
||||
<div class="name">{column.columnName}</div>
|
||||
<div class="nowrap">
|
||||
<span class="icon" on:click={onEdit}>
|
||||
<FontIcon icon="icon edit" />
|
||||
</span>
|
||||
<span class="icon" on:click={onRemove}>
|
||||
<FontIcon icon="icon delete" />
|
||||
</span>
|
||||
<span class="icon" on:click={onUp}>
|
||||
<FontIcon icon="icon arrow-up" />
|
||||
</span>
|
||||
<span class="icon" on:click={onDown}>
|
||||
<FontIcon icon="icon arrow-down" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row:hover {
|
||||
background-color: var(--theme-bg-selected);
|
||||
}
|
||||
.name {
|
||||
white-space: nowrap;
|
||||
margin: 5px;
|
||||
}
|
||||
.icon {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
.icon:hover {
|
||||
background-color: var(--theme-bg-3);
|
||||
}
|
||||
</style>
|
||||
55
packages/web/src/freetable/ColumnNameEditor.svelte
Normal file
55
packages/web/src/freetable/ColumnNameEditor.svelte
Normal file
@@ -0,0 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import keycodes from '../utility/keycodes';
|
||||
|
||||
export let onEnter;
|
||||
export let onBlur = undefined;
|
||||
export let focusOnCreate = false;
|
||||
export let blurOnEnter = false;
|
||||
export let existingNames;
|
||||
export let defaultValue = '';
|
||||
|
||||
let domEditor;
|
||||
let value = defaultValue || '';
|
||||
$: isError = value && existingNames && existingNames.includes(value);
|
||||
|
||||
const handleKeyDown = event => {
|
||||
if (value && event.keyCode == keycodes.enter && !isError) {
|
||||
onEnter(value);
|
||||
value = '';
|
||||
if (blurOnEnter) domEditor.blur();
|
||||
}
|
||||
if (event.keyCode == keycodes.escape) {
|
||||
value = '';
|
||||
domEditor.blur();
|
||||
}
|
||||
};
|
||||
const handleBlur = () => {
|
||||
if (value && !isError) {
|
||||
onEnter(value);
|
||||
value = '';
|
||||
}
|
||||
if (onBlur) onBlur();
|
||||
};
|
||||
if (focusOnCreate) onMount(() => domEditor.focus());
|
||||
</script>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
{...$$restProps}
|
||||
bind:value
|
||||
bind:this={domEditor}
|
||||
on:keydown={handleKeyDown}
|
||||
on:blur={handleBlur}
|
||||
class:isError
|
||||
/>
|
||||
|
||||
<style>
|
||||
input {
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
|
||||
input.isError {
|
||||
background: var(--theme-bg-red);
|
||||
}
|
||||
</style>
|
||||
82
packages/web/src/freetable/FreeTableColumnEditor.svelte
Normal file
82
packages/web/src/freetable/FreeTableColumnEditor.svelte
Normal file
@@ -0,0 +1,82 @@
|
||||
<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>
|
||||
72
packages/web/src/freetable/FreeTableGrid.svelte
Normal file
72
packages/web/src/freetable/FreeTableGrid.svelte
Normal file
@@ -0,0 +1,72 @@
|
||||
<script lang="ts" context="module">
|
||||
function extractMacroValuesForMacro(macroValues, macro) {
|
||||
if (!macro) return {};
|
||||
return {
|
||||
..._.fromPairs((macro.args || []).filter(x => x.default != null).map(x => [x.name, x.default])),
|
||||
..._.mapKeys(macroValues, (v, k) => k.replace(/^.*#/, '')),
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
||||
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
|
||||
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
||||
import ColumnManager from '../datagrid/ColumnManager.svelte';
|
||||
import ReferenceManager from '../datagrid/ReferenceManager.svelte';
|
||||
import FreeTableGridCore from './FreeTableGridCore.svelte';
|
||||
import FreeTableColumnEditor from './FreeTableColumnEditor.svelte';
|
||||
|
||||
let managerSize;
|
||||
let selectedMacro;
|
||||
</script>
|
||||
|
||||
<HorizontalSplitter initialValue="300px" bind:size={managerSize}>
|
||||
<div class="left" slot="1">
|
||||
<WidgetColumnBar>
|
||||
<WidgetColumnBarItem title="Columns" name="columns" height="40%">
|
||||
<FreeTableColumnEditor {...$$props} {managerSize} />
|
||||
</WidgetColumnBarItem>
|
||||
|
||||
<!-- <WidgetColumnBarItem title="Macros" name="macros">
|
||||
<MacroManager {...$$props} {managerSize} />
|
||||
</WidgetColumnBarItem> -->
|
||||
</WidgetColumnBar>
|
||||
</div>
|
||||
<div class="grid" slot="2">
|
||||
<VerticalSplitter initialValue="70%">
|
||||
<svelte:fragment slot="1">
|
||||
<FreeTableGridCore {...$$props} />
|
||||
</svelte:fragment>
|
||||
|
||||
<!-- macroPreview={selectedMacro}
|
||||
macroValues={extractMacroValuesForMacro(macroValues, selectedMacro)}
|
||||
onSelectionChanged={setSelectedCells}
|
||||
{setSelectedMacro} -->
|
||||
|
||||
<!-- {#if selectedMacro}
|
||||
<MacroDetail
|
||||
{selectedMacro}
|
||||
{setSelectedMacro}
|
||||
onChangeValues={setMacroValues}
|
||||
{macroValues}
|
||||
onExecute={handleExecuteMacro}
|
||||
/>
|
||||
{/if} -->
|
||||
</VerticalSplitter>
|
||||
</div>
|
||||
</HorizontalSplitter>
|
||||
|
||||
<style>
|
||||
.left {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
background-color: var(--theme-bg-0);
|
||||
}
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
||||
39
packages/web/src/freetable/FreeTableGridCore.svelte
Normal file
39
packages/web/src/freetable/FreeTableGridCore.svelte
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { createGridCache, FreeTableGridDisplay } from 'dbgate-datalib';
|
||||
import { writable } from 'svelte/store';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
|
||||
import DataGridCore from '../datagrid/DataGridCore.svelte';
|
||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import axiosInstance from '../utility/axiosInstance';
|
||||
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;
|
||||
|
||||
let selectedCells = [];
|
||||
const cache = writable(createGridCache());
|
||||
|
||||
$: grider = macroPreview
|
||||
? new MacroPreviewGrider(modelState.value, macroPreview, macroValues, selectedCells)
|
||||
: new FreeTableGrider(modelState, dispatchModel);
|
||||
$: display = new FreeTableGridDisplay(grider.model || modelState.value, config, setConfig, $cache, cache.update);
|
||||
|
||||
async function exportGrid() {
|
||||
const jslid = uuidv1();
|
||||
await axiosInstance.post('jsldata/save-free-table', { jslid, data: modelState.value });
|
||||
const initialValues: any = {};
|
||||
initialValues.sourceStorageType = 'jsldata';
|
||||
initialValues.sourceJslId = jslid;
|
||||
initialValues.sourceList = ['editor-data'];
|
||||
showModal(ImportExportModal, { initialValues: initialValues });
|
||||
}
|
||||
</script>
|
||||
|
||||
<DataGridCore {...$$props} {grider} {display} frameSelection={!!macroPreview} {exportGrid} onExportGrid={exportGrid} />
|
||||
85
packages/web/src/freetable/FreeTableGrider.ts
Normal file
85
packages/web/src/freetable/FreeTableGrider.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { FreeTableModel } from 'dbgate-datalib';
|
||||
import Grider, { GriderRowStatus } 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)),
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
40
packages/web/src/freetable/MacroPreviewGrider.ts
Normal file
40
packages/web/src/freetable/MacroPreviewGrider.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { FreeTableModel, MacroDefinition, MacroSelectedCell, runMacro } from 'dbgate-datalib';
|
||||
import Grider, { GriderRowStatus } from '../datagrid/Grider';
|
||||
import _ from 'lodash';
|
||||
|
||||
function convertToSet(row, field) {
|
||||
if (!row) return null;
|
||||
if (!row[field]) return null;
|
||||
if (_.isSet(row[field])) return row[field];
|
||||
return new Set(row[field]);
|
||||
}
|
||||
|
||||
export default class MacroPreviewGrider extends Grider {
|
||||
model: FreeTableModel;
|
||||
_errors: string[] = [];
|
||||
constructor(model: FreeTableModel, macro: MacroDefinition, macroArgs: {}, selectedCells: MacroSelectedCell[]) {
|
||||
super();
|
||||
this.model = runMacro(macro, macroArgs, model, true, selectedCells, this._errors);
|
||||
}
|
||||
|
||||
get errors() {
|
||||
return this._errors;
|
||||
}
|
||||
|
||||
getRowStatus(index): GriderRowStatus {
|
||||
const row = this.model.rows[index];
|
||||
return {
|
||||
status: (row && row.__rowStatus) || 'regular',
|
||||
modifiedFields: convertToSet(row, '__modifiedFields'),
|
||||
insertedFields: convertToSet(row, '__insertedFields'),
|
||||
deletedFields: convertToSet(row, '__deletedFields'),
|
||||
};
|
||||
}
|
||||
|
||||
getRowData(index: any) {
|
||||
return this.model.rows[index];
|
||||
}
|
||||
get rowCount() {
|
||||
return this.model.rows.length;
|
||||
}
|
||||
}
|
||||
273
packages/web/src/freetable/macros.js
Normal file
273
packages/web/src/freetable/macros.js
Normal file
@@ -0,0 +1,273 @@
|
||||
const macros = [
|
||||
{
|
||||
title: 'Remove diacritics',
|
||||
name: 'removeDiacritics',
|
||||
group: 'Text',
|
||||
description: 'Removes diacritics from selected cells',
|
||||
type: 'transformValue',
|
||||
code: `return modules.lodash.deburr(value)`,
|
||||
},
|
||||
{
|
||||
title: 'Search & replace text',
|
||||
name: 'stringReplace',
|
||||
group: 'Text',
|
||||
description: 'Search & replace text or regular expression',
|
||||
type: 'transformValue',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Find',
|
||||
name: 'find',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Replace with',
|
||||
name: 'replace',
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
label: 'Case sensitive',
|
||||
name: 'caseSensitive',
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
label: 'Regular expression',
|
||||
name: 'isRegex',
|
||||
},
|
||||
],
|
||||
code: `
|
||||
const rtext = args.isRegex ? args.find : modules.lodash.escapeRegExp(args.find);
|
||||
const rflags = args.caseSensitive ? 'g' : 'ig';
|
||||
return value ? value.toString().replace(new RegExp(rtext, rflags), 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: 'type',
|
||||
default: 'toUpper',
|
||||
},
|
||||
],
|
||||
code: `return modules.lodash[args.type](value)`,
|
||||
},
|
||||
{
|
||||
title: 'Row index',
|
||||
name: 'rowIndex',
|
||||
group: 'Tools',
|
||||
description: 'Index of row from 1 (autoincrement)',
|
||||
type: 'transformValue',
|
||||
code: `return rowIndex + 1`,
|
||||
},
|
||||
{
|
||||
title: 'Generate UUID',
|
||||
name: 'uuidv1',
|
||||
group: 'Tools',
|
||||
description: 'Generate unique identifier',
|
||||
type: 'transformValue',
|
||||
args: [
|
||||
{
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: 'uuidv1', name: 'V1 - from timestamp' },
|
||||
{ value: 'uuidv4', name: 'V4 - random generated' },
|
||||
],
|
||||
label: 'Version',
|
||||
name: 'version',
|
||||
default: 'uuidv1',
|
||||
},
|
||||
],
|
||||
code: `return modules[args.version]()`,
|
||||
},
|
||||
{
|
||||
title: 'Current date',
|
||||
name: 'currentDate',
|
||||
group: 'Tools',
|
||||
description: 'Gets current date',
|
||||
type: 'transformValue',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Format',
|
||||
name: 'format',
|
||||
default: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
],
|
||||
code: `return modules.moment().format(args.format)`,
|
||||
},
|
||||
{
|
||||
title: 'Duplicate rows',
|
||||
name: 'duplicateRows',
|
||||
group: 'Tools',
|
||||
description: 'Duplicate selected rows',
|
||||
type: 'transformRows',
|
||||
code: `
|
||||
const selectedRowIndexes = modules.lodash.uniq(selectedCells.map(x => x.row));
|
||||
const selectedRows = modules.lodash.groupBy(selectedCells, 'row');
|
||||
const maxIndex = modules.lodash.max(selectedRowIndexes);
|
||||
return [
|
||||
...rows.slice(0, maxIndex + 1),
|
||||
...selectedRowIndexes.map(index => ({
|
||||
...modules.lodash.pick(rows[index], selectedRows[index].map(x => x.column)),
|
||||
__rowStatus: 'inserted',
|
||||
})),
|
||||
...rows.slice(maxIndex + 1),
|
||||
]
|
||||
`,
|
||||
},
|
||||
{
|
||||
title: 'Delete empty rows',
|
||||
name: 'deleteEmptyRows',
|
||||
group: 'Tools',
|
||||
description: 'Delete empty rows - rows with all values null or empty string',
|
||||
type: 'transformRows',
|
||||
code: `
|
||||
return rows.map(row => {
|
||||
if (cols.find(col => row[col])) return row;
|
||||
return {
|
||||
...row,
|
||||
__rowStatus: 'deleted',
|
||||
};
|
||||
})
|
||||
`,
|
||||
},
|
||||
{
|
||||
title: 'Duplicate columns',
|
||||
name: 'duplicateColumns',
|
||||
group: 'Tools',
|
||||
description: 'Duplicate selected columns',
|
||||
type: 'transformData',
|
||||
code: `
|
||||
const selectedColumnNames = modules.lodash.uniq(selectedCells.map(x => x.column));
|
||||
const selectedRowIndexes = modules.lodash.uniq(selectedCells.map(x => x.row));
|
||||
const addedColumnNames = selectedColumnNames.map(col => (args.prefix || '') + col + (args.postfix || ''));
|
||||
const resultRows = rows.map((row, rowIndex) => ({
|
||||
...row,
|
||||
...(selectedRowIndexes.includes(rowIndex) ? modules.lodash.fromPairs(selectedColumnNames.map(col => [(args.prefix || '') + col + (args.postfix || ''), row[col]])) : {}),
|
||||
__insertedFields: addedColumnNames,
|
||||
}));
|
||||
const resultCols = [
|
||||
...cols,
|
||||
...addedColumnNames,
|
||||
];
|
||||
return {
|
||||
rows: resultRows,
|
||||
cols: resultCols,
|
||||
}
|
||||
`,
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Prefix',
|
||||
name: 'prefix',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Postfix',
|
||||
name: 'postfix',
|
||||
default: '_copy',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Extract date fields',
|
||||
name: 'extractDateFields',
|
||||
group: 'Tools',
|
||||
description: 'Extract yaear, month, day and other date/time fields from selection and adds it as new columns',
|
||||
type: 'transformData',
|
||||
code: `
|
||||
const selectedColumnNames = modules.lodash.uniq(selectedCells.map(x => x.column));
|
||||
const selectedRowIndexes = modules.lodash.uniq(selectedCells.map(x => x.row));
|
||||
const addedColumnNames = modules.lodash.compact([args.year, args.month, args.day, args.hour, args.minute, args.second]);
|
||||
const selectedRows = modules.lodash.groupBy(selectedCells, 'row');
|
||||
const resultRows = rows.map((row, rowIndex) => {
|
||||
if (!selectedRowIndexes.includes(rowIndex)) return {
|
||||
...row,
|
||||
__insertedFields: addedColumnNames,
|
||||
};
|
||||
let mom = null;
|
||||
for(const cell of selectedRows[rowIndex]) {
|
||||
const m = modules.moment(row[cell.column]);
|
||||
if (m.isValid()) {
|
||||
mom = m;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mom) return {
|
||||
...row,
|
||||
__insertedFields: addedColumnNames,
|
||||
};
|
||||
|
||||
const fields = {
|
||||
[args.year]: mom.year(),
|
||||
[args.month]: mom.month() + 1,
|
||||
[args.day]: mom.day(),
|
||||
[args.hour]: mom.hour(),
|
||||
[args.minute]: mom.minute(),
|
||||
[args.second]: mom.second(),
|
||||
};
|
||||
|
||||
return {
|
||||
...row,
|
||||
...modules.lodash.pick(fields, addedColumnNames),
|
||||
__insertedFields: addedColumnNames,
|
||||
}
|
||||
});
|
||||
const resultCols = [
|
||||
...cols,
|
||||
...addedColumnNames,
|
||||
];
|
||||
return {
|
||||
rows: resultRows,
|
||||
cols: resultCols,
|
||||
}
|
||||
`,
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Year name',
|
||||
name: 'year',
|
||||
default: 'year',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Month name',
|
||||
name: 'month',
|
||||
default: 'month',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Day name',
|
||||
name: 'day',
|
||||
default: 'day',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Hour name',
|
||||
name: 'hour',
|
||||
default: 'hour',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Minute name',
|
||||
name: 'minute',
|
||||
default: 'minute',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: 'Second name',
|
||||
name: 'second',
|
||||
default: 'second',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default macros;
|
||||
Reference in New Issue
Block a user