diff --git a/packages/datalib/src/ChangeSet.ts b/packages/datalib/src/ChangeSet.ts
new file mode 100644
index 000000000..7eacb9eab
--- /dev/null
+++ b/packages/datalib/src/ChangeSet.ts
@@ -0,0 +1,99 @@
+import _ from 'lodash';
+
+export interface ChangeSetItem {
+ pureName: string;
+ schemaName: string;
+ insertedRowIndex?: number;
+ condition?: { [column: string]: string };
+ fields?: { [column: string]: string };
+}
+
+export interface ChangeSet {
+ inserts: ChangeSetItem[];
+ updates: ChangeSetItem[];
+ deletes: ChangeSetItem[];
+}
+
+export function createChangeSet(): ChangeSet {
+ return {
+ inserts: [],
+ updates: [],
+ deletes: [],
+ };
+}
+
+export interface ChangeSetFieldDefinition {
+ pureName: string;
+ schemaName: string;
+ uniqueName: string;
+ columnName: string;
+ insertedRowIndex?: number;
+ condition?: { [column: string]: string };
+}
+
+function findExistingChangeSetItem(
+ changeSet: ChangeSet,
+ definition: ChangeSetFieldDefinition
+): [keyof ChangeSet, ChangeSetItem] {
+ if (definition.insertedRowIndex != null) {
+ return [
+ 'inserts',
+ changeSet.inserts.find(
+ x =>
+ x.pureName == definition.pureName &&
+ x.schemaName == definition.schemaName &&
+ x.insertedRowIndex == definition.insertedRowIndex
+ ),
+ ];
+ } else {
+ return [
+ 'updates',
+ changeSet.updates.find(
+ x =>
+ x.pureName == definition.pureName &&
+ x.schemaName == definition.schemaName &&
+ _.isEqual(x.condition, definition.condition)
+ ),
+ ];
+ }
+}
+
+export function setChangeSetValue(
+ changeSet: ChangeSet,
+ definition: ChangeSetFieldDefinition,
+ value: string
+): ChangeSet {
+ const [fieldName, existingItem] = findExistingChangeSetItem(changeSet, definition);
+ if (existingItem) {
+ return {
+ ...changeSet,
+ [fieldName]: changeSet[fieldName].map(item =>
+ item == existingItem
+ ? {
+ ...item,
+ fields: {
+ ...item.fields,
+ [definition.uniqueName]: value,
+ },
+ }
+ : item
+ ),
+ };
+ }
+
+ return {
+ ...changeSet,
+ [fieldName]: [
+ ...changeSet[fieldName],
+ {
+ pureName: definition.pureName,
+ schemaName: definition.schemaName,
+ condition: definition.condition,
+ insertedRowIndex: definition.insertedRowIndex,
+ fields: {
+ [definition.uniqueName]: value,
+ },
+ },
+ ],
+ };
+}
diff --git a/packages/datalib/src/GridDisplay.ts b/packages/datalib/src/GridDisplay.ts
index 2d4c67c3c..ae30a973e 100644
--- a/packages/datalib/src/GridDisplay.ts
+++ b/packages/datalib/src/GridDisplay.ts
@@ -4,6 +4,7 @@ import { ForeignKeyInfo, TableInfo, ColumnInfo, DbType } from '@dbgate/types';
import { parseFilter, getFilterType } from '@dbgate/filterparser';
import { filterName } from './filterName';
import { Select, Expression } from '@dbgate/sqltree';
+import { ChangeSetFieldDefinition } from './ChangeSet';
export interface DisplayColumn {
schemaName: string;
@@ -47,6 +48,8 @@ export abstract class GridDisplay {
) {}
abstract getPageQuery(offset: number, count: number): string;
columns: DisplayColumn[];
+ baseTable?: TableInfo;
+ changeSetKeyFields: string[] = null;
setColumnVisibility(uniquePath: string[], isVisible: boolean) {
const uniqueName = uniquePath.join('.');
if (uniquePath.length == 1) {
@@ -350,4 +353,23 @@ export abstract class GridDisplay {
});
this.reload();
}
+
+ getChangeSetCondition(row) {
+ if (!this.changeSetKeyFields) return null;
+ return _.pick(row, this.changeSetKeyFields);
+ }
+
+ getChangeSetField(row, uniqueName): ChangeSetFieldDefinition {
+ const col = this.columns.find(x => x.uniqueName == uniqueName);
+ if (!col) return null;
+ if (!this.baseTable) return null;
+ if (this.baseTable.pureName != col.pureName || this.baseTable.schemaName != col.schemaName) return null;
+ return {
+ pureName: col.pureName,
+ schemaName: col.schemaName,
+ uniqueName: uniqueName,
+ columnName: col.columnName,
+ condition: this.getChangeSetCondition(row),
+ };
+ }
}
diff --git a/packages/datalib/src/TableGridDisplay.ts b/packages/datalib/src/TableGridDisplay.ts
index 3c21f0aa9..acc85335c 100644
--- a/packages/datalib/src/TableGridDisplay.ts
+++ b/packages/datalib/src/TableGridDisplay.ts
@@ -1,4 +1,4 @@
-import _ from 'lodash'
+import _ from 'lodash';
import { GridDisplay, combineReferenceActions } from './GridDisplay';
import { Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree';
import { TableInfo, EngineDriver } from '@dbgate/types';
@@ -16,6 +16,10 @@ export class TableGridDisplay extends GridDisplay {
) {
super(config, setConfig, cache, setCache, getTableInfo);
this.columns = this.getDisplayColumns(table, []);
+ this.baseTable = table;
+ this.changeSetKeyFields = table.primaryKey
+ ? table.primaryKey.columns.map(x => x.columnName)
+ : table.columns.map(x => x.columnName);
}
createSelect() {
diff --git a/packages/datalib/src/index.ts b/packages/datalib/src/index.ts
index a37b6f16d..2f4c51446 100644
--- a/packages/datalib/src/index.ts
+++ b/packages/datalib/src/index.ts
@@ -1,4 +1,5 @@
export * from "./GridDisplay";
export * from "./GridConfig";
export * from "./TableGridDisplay";
+export * from "./ChangeSet";
export * from "./filterName";
diff --git a/packages/web/src/datagrid/ColumnManager.js b/packages/web/src/datagrid/ColumnManager.js
index 08585b804..6533e686e 100644
--- a/packages/web/src/datagrid/ColumnManager.js
+++ b/packages/web/src/datagrid/ColumnManager.js
@@ -21,12 +21,15 @@ const SearchBoxWrapper = styled.div`
`;
const Button = styled.button`
- width: 50px;
+// -webkit-appearance: none;
+// -moz-appearance: none;
+// appearance: none;
+ // width: 50px;
`;
const Input = styled.input`
flex: 1;
- width: 80px;
+ min-width: 90px;
`;
function ExpandIcon({ display, column, isHover, ...other }) {
diff --git a/packages/web/src/datagrid/DataFilterControl.js b/packages/web/src/datagrid/DataFilterControl.js
index 1ad2fb717..2b2f86e02 100644
--- a/packages/web/src/datagrid/DataFilterControl.js
+++ b/packages/web/src/datagrid/DataFilterControl.js
@@ -34,7 +34,7 @@ const FilterDiv = styled.div`
`;
const FilterInput = styled.input`
flex: 1;
- width: 10px;
+ min-width: 10px;
background-color: ${props => (props.state == 'ok' ? '#CCFFCC' : props.state == 'error' ? '#FFCCCC' : 'white')};
`;
const FilterButton = styled.button`
diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js
index 9958f43ac..1146b4828 100644
--- a/packages/web/src/datagrid/DataGridCore.js
+++ b/packages/web/src/datagrid/DataGridCore.js
@@ -21,6 +21,7 @@ import {
emptyCellArray,
} from './selection';
import keycodes from '../utility/keycodes';
+import InplaceEditor from './InplaceEditor';
const GridContainer = styled.div`
position: absolute;
@@ -101,7 +102,6 @@ const NullSpan = styled.span`
color: gray;
font-style: italic;
`;
-
const wheelRowCount = 5;
function CellFormattedValue({ value }) {
@@ -112,7 +112,7 @@ function CellFormattedValue({ value }) {
/** @param props {import('./types').DataGridProps} */
export default function DataGridCore(props) {
- const { conid, database, display, tabVisible } = props;
+ const { conid, database, display, changeSet, tabVisible } = props;
const columns = display.getGridColumns();
// console.log(`GRID, conid=${conid}, database=${database}, sql=${sql}`);
@@ -137,6 +137,8 @@ export default function DataGridCore(props) {
const [dragStartCell, setDragStartCell] = React.useState(nullCell);
const [shiftDragStartCell, setShiftDragStartCell] = React.useState(nullCell);
+ const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell);
+
const loadNextData = async () => {
if (isLoading) return;
setLoadProps({
@@ -323,7 +325,12 @@ export default function DataGridCore(props) {
setCurrentCell(cell);
setSelectedCells(getCellRange(cell, cell));
setDragStartCell(cell);
- // console.log('START', cell);
+
+ if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorCell) && _.isEqual(cell, currentCell)) {
+ setInplaceEditorCell(cell);
+ } else if (!_.isEqual(cell, inplaceEditorCell)) {
+ setInplaceEditorCell(null);
+ }
}
function handleGridMouseMove(event) {
@@ -641,8 +648,22 @@ export default function DataGridCore(props) {
// @ts-ignore
isSelected={cellIsSelected(firstVisibleRowScrollIndex + index, col.colIndex)}
>
-
- {col.hintColumnName && {row[col.hintColumnName]}}
+ {inplaceEditorCell &&
+ firstVisibleRowScrollIndex + index == inplaceEditorCell[0] &&
+ col.colIndex == inplaceEditorCell[1] ? (
+
+ ) : (
+ <>
+
+ {col.hintColumnName && {row[col.hintColumnName]}}
+ >
+ )}
))}
diff --git a/packages/web/src/datagrid/InplaceEditor.js b/packages/web/src/datagrid/InplaceEditor.js
new file mode 100644
index 000000000..12da1cb0b
--- /dev/null
+++ b/packages/web/src/datagrid/InplaceEditor.js
@@ -0,0 +1,39 @@
+import _ from 'lodash';
+import React from 'react';
+import styled from 'styled-components';
+import theme from '../theme';
+import keycodes from '../utility/keycodes';
+import { setChangeSetValue } from '@dbgate/datalib';
+
+const StyledInput = styled.input`
+ border: 0px solid;
+ outline: none;
+ margin: 0px;
+ padding: 0px;
+`;
+
+export default function InplaceEditor({ widthPx, value, definition, changeSet, setChangeSet }) {
+ const editorRef = React.useRef();
+ React.useEffect(() => {
+ const editor = editorRef.current;
+ editor.value = value;
+ editor.focus();
+ editor.select();
+ }, []);
+ function handleBlur() {
+ const editor = editorRef.current;
+ setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
+ }
+ return (
+
+ );
+}
diff --git a/packages/web/src/datagrid/selection.ts b/packages/web/src/datagrid/selection.ts
index 28ff9384a..2c4e3d693 100644
--- a/packages/web/src/datagrid/selection.ts
+++ b/packages/web/src/datagrid/selection.ts
@@ -67,3 +67,4 @@ export function cellFromEvent(event): CellAddress {
const row = cell.getAttribute('data-row');
return convertCellAddress(row, col);
}
+
diff --git a/packages/web/src/datagrid/types.ts b/packages/web/src/datagrid/types.ts
index ef43b79b8..5ca7774e6 100644
--- a/packages/web/src/datagrid/types.ts
+++ b/packages/web/src/datagrid/types.ts
@@ -1,8 +1,10 @@
-import { GridDisplay } from '@dbgate/datalib';
+import { GridDisplay, ChangeSet } from '@dbgate/datalib';
export interface DataGridProps {
conid: number;
database: string;
display: GridDisplay;
tabVisible?: boolean;
+ changeSet?: ChangeSet;
+ setChangeSet?: Function;
}
diff --git a/packages/web/src/tabs/TableDataTab.js b/packages/web/src/tabs/TableDataTab.js
index 32eac4f1b..2f13e858b 100644
--- a/packages/web/src/tabs/TableDataTab.js
+++ b/packages/web/src/tabs/TableDataTab.js
@@ -3,7 +3,7 @@ import useFetch from '../utility/useFetch';
import styled from 'styled-components';
import theme from '../theme';
import DataGrid from '../datagrid/DataGrid';
-import { TableGridDisplay, createGridConfig, createGridCache } from '@dbgate/datalib';
+import { TableGridDisplay, createGridConfig, createGridCache, createChangeSet } from '@dbgate/datalib';
import useTableInfo from '../utility/useTableInfo';
import useConnectionInfo from '../utility/useConnectionInfo';
import engines from '@dbgate/engines';
@@ -13,6 +13,10 @@ export default function TableDataTab({ conid, database, schemaName, pureName, ta
const tableInfo = useTableInfo({ conid, database, schemaName, pureName });
const [config, setConfig] = React.useState(createGridConfig());
const [cache, setCache] = React.useState(createGridCache());
+ const [changeSet, setChangeSet] = React.useState(createChangeSet());
+
+ console.log('changeSet', changeSet);
+
const connection = useConnectionInfo(conid);
if (!tableInfo || !connection) return null;
const display = new TableGridDisplay(tableInfo, engines(connection), config, setConfig, cache, setCache, name =>
@@ -25,6 +29,8 @@ export default function TableDataTab({ conid, database, schemaName, pureName, ta
database={database}
display={display}
tabVisible={tabVisible}
+ changeSet={changeSet}
+ setChangeSet={setChangeSet}
/>
);
}