editing in form view

This commit is contained in:
Jan Prochazka
2021-01-10 16:50:08 +01:00
parent c5c4b0b6de
commit fe1180d1e4
7 changed files with 207 additions and 14 deletions

View File

@@ -11,6 +11,7 @@ import { ChangeCacheFunc, ChangeConfigFunc, DisplayColumn } from './GridDisplay'
export class FormViewDisplay { export class FormViewDisplay {
isLoadedCorrectly = true; isLoadedCorrectly = true;
columns: DisplayColumn[]; columns: DisplayColumn[];
public baseTable: TableInfo;
constructor( constructor(
public config: GridConfig, public config: GridConfig,
@@ -20,4 +21,5 @@ export class FormViewDisplay {
public driver?: EngineDriver, public driver?: EngineDriver,
public dbinfo: DatabaseInfo = null public dbinfo: DatabaseInfo = null
) {} ) {}
} }

View File

@@ -15,9 +15,9 @@ import {
import { filterName } from './filterName'; import { filterName } from './filterName';
import { TableGridDisplay } from './TableGridDisplay'; import { TableGridDisplay } from './TableGridDisplay';
import stableStringify from 'json-stable-stringify'; import stableStringify from 'json-stable-stringify';
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
export class TableFormViewDisplay extends FormViewDisplay { export class TableFormViewDisplay extends FormViewDisplay {
public table: TableInfo;
// use utility functions from GridDisplay and publish result in FromViewDisplat interface // use utility functions from GridDisplay and publish result in FromViewDisplat interface
private gridDisplay: TableGridDisplay; private gridDisplay: TableGridDisplay;
@@ -35,13 +35,17 @@ export class TableFormViewDisplay extends FormViewDisplay {
this.isLoadedCorrectly = this.gridDisplay.isLoadedCorrectly; this.isLoadedCorrectly = this.gridDisplay.isLoadedCorrectly;
this.columns = this.gridDisplay.columns; this.columns = this.gridDisplay.columns;
this.baseTable = this.gridDisplay.baseTable;
} }
getPrimaryKeyEqualCondition(): Condition { getPrimaryKeyEqualCondition(row = null): Condition {
if (!this.config.formViewKey) return null; if (!row) row = this.config.formViewKey;
if (!row) return null;
const { primaryKey } = this.gridDisplay.baseTable;
if (primaryKey) return null;
return { return {
conditionType: 'and', conditionType: 'and',
conditions: _.keys(this.config.formViewKey).map((columnName) => ({ conditions: primaryKey.columns.map(({ columnName }) => ({
conditionType: 'binary', conditionType: 'binary',
operator: '=', operator: '=',
left: { left: {
@@ -186,4 +190,25 @@ export class TableFormViewDisplay extends FormViewDisplay {
const sql = treeToSql(this.driver, select, dumpSqlSelect); const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql; return sql;
} }
getChangeSetRow(row): ChangeSetRowDefinition {
if (!this.baseTable) return null;
return {
pureName: this.baseTable.pureName,
schemaName: this.baseTable.schemaName,
condition: this.extractKey(row),
};
}
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 {
...this.getChangeSetRow(row),
uniqueName: uniqueName,
columnName: col.columnName,
};
}
} }

View File

@@ -23,6 +23,7 @@ export default function InplaceEditor({
onSetValue, onSetValue,
}) { }) {
const editorRef = React.useRef(); const editorRef = React.useRef();
const widthRef = React.useRef(widthPx);
const isChangedRef = React.useRef(!!inplaceEditorState.text); const isChangedRef = React.useRef(!!inplaceEditorState.text);
React.useEffect(() => { React.useEffect(() => {
const editor = editorRef.current; const editor = editorRef.current;
@@ -88,9 +89,9 @@ export default function InplaceEditor({
onChange={() => (isChangedRef.current = true)} onChange={() => (isChangedRef.current = true)}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
style={{ style={{
width: widthPx, width: widthRef.current,
minWidth: widthPx, minWidth: widthRef.current,
maxWidth: widthPx, maxWidth: widthRef.current,
}} }}
/> />
); );

View File

@@ -0,0 +1,93 @@
import {
ChangeSet,
changeSetContainsChanges,
changeSetInsertNewRow,
createChangeSet,
deleteChangeSetRows,
findExistingChangeSetItem,
getChangeSetInsertedRows,
TableFormViewDisplay,
revertChangeSetRowChanges,
setChangeSetValue,
ChangeSetRowDefinition,
} from 'dbgate-datalib';
import Former from './Former';
export default class ChangeSetFormer extends Former {
public changeSet: ChangeSet;
public setChangeSet: Function;
private batchChangeSet: ChangeSet;
public rowDefinition: ChangeSetRowDefinition;
public rowStatus;
constructor(
public sourceRow: any,
public changeSetState,
public dispatchChangeSet,
public display: TableFormViewDisplay
) {
super();
this.changeSet = changeSetState && changeSetState.value;
this.setChangeSet = (value) => dispatchChangeSet({ type: 'set', value });
this.batchChangeSet = null;
this.rowDefinition = display.getChangeSetRow(sourceRow);
const [matchedField, matchedChangeSetItem] = findExistingChangeSetItem(this.changeSet, this.rowDefinition);
this.rowData = matchedChangeSetItem ? { ...sourceRow, ...matchedChangeSetItem.fields } : sourceRow;
let status = 'regular';
if (matchedChangeSetItem && matchedField == 'updates') status = 'updated';
if (matchedField == 'deletes') status = 'deleted';
this.rowStatus = {
status,
modifiedFields:
matchedChangeSetItem && matchedChangeSetItem.fields ? new Set(Object.keys(matchedChangeSetItem.fields)) : null,
};
}
applyModification(changeSetReducer) {
if (this.batchChangeSet) {
this.batchChangeSet = changeSetReducer(this.batchChangeSet);
} else {
this.setChangeSet(changeSetReducer(this.changeSet));
}
}
setCellValue( uniqueName: string, value: any) {
const row = this.sourceRow;
const definition = this.display.getChangeSetField(row, uniqueName);
this.applyModification((chs) => setChangeSetValue(chs, definition, value));
}
deleteRow(index: number) {
this.applyModification((chs) => deleteChangeSetRows(chs, this.rowDefinition));
}
beginUpdate() {
this.batchChangeSet = this.changeSet;
}
endUpdate() {
this.setChangeSet(this.batchChangeSet);
this.batchChangeSet = null;
}
revertRowChanges() {
this.applyModification((chs) => revertChangeSetRowChanges(chs, this.rowDefinition));
}
revertAllChanges() {
this.applyModification((chs) => createChangeSet());
}
undo() {
this.dispatchChangeSet({ type: 'undo' });
}
redo() {
this.dispatchChangeSet({ type: 'redo' });
}
get canUndo() {
return this.changeSetState.canUndo;
}
get canRedo() {
return this.changeSetState.canRedo;
}
get containsChanges() {
return changeSetContainsChanges(this.changeSet);
}
}

View File

@@ -1,3 +1,5 @@
// @ts-nocheck
import _ from 'lodash'; import _ from 'lodash';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
@@ -48,7 +50,6 @@ const TableHeaderCell = styled.td`
position: relative; position: relative;
${(props) => ${(props) =>
// @ts-ignore
props.isSelected && props.isSelected &&
` `
background: initial; background: initial;
@@ -66,12 +67,17 @@ const TableBodyCell = styled.td`
overflow: hidden; overflow: hidden;
${(props) => ${(props) =>
// @ts-ignore
props.isSelected && props.isSelected &&
` `
background: initial; background: initial;
background-color: ${props.theme.gridbody_selection[4]}; background-color: ${props.theme.gridbody_selection[4]};
color: ${props.theme.gridbody_invfont1};`} color: ${props.theme.gridbody_invfont1};`}
${(props) =>
!props.isSelected &&
props.isModifiedCell &&
`
background-color: ${props.theme.gridbody_background_orange[1]};`}
`; `;
const FocusField = styled.input` const FocusField = styled.input`
@@ -86,7 +92,7 @@ function isDataCell(cell) {
} }
export default function FormView(props) { export default function FormView(props) {
const { rowData, toolbarPortalRef, tabVisible, config, setConfig, onNavigate } = props; const { toolbarPortalRef, tabVisible, config, setConfig, onNavigate, former } = props;
/** @type {import('dbgate-datalib').FormViewDisplay} */ /** @type {import('dbgate-datalib').FormViewDisplay} */
const formDisplay = props.formDisplay; const formDisplay = props.formDisplay;
const theme = useTheme(); const theme = useTheme();
@@ -100,6 +106,8 @@ export default function FormView(props) {
const rowCount = Math.floor((wrapperHeight - 20) / rowHeight); const rowCount = Math.floor((wrapperHeight - 20) / rowHeight);
const columnChunks = _.chunk(formDisplay.columns, rowCount); const columnChunks = _.chunk(formDisplay.columns, rowCount);
const { rowData, rowStatus } = former;
const handleSwitchToTable = () => { const handleSwitchToTable = () => {
setConfig((cfg) => ({ setConfig((cfg) => ({
...cfg, ...cfg,
@@ -318,6 +326,7 @@ export default function FormView(props) {
data-col={chunkIndex * 2 + 1} data-col={chunkIndex * 2 + 1}
// @ts-ignore // @ts-ignore
isSelected={currentCell[0] == rowIndex && currentCell[1] == chunkIndex * 2 + 1} isSelected={currentCell[0] == rowIndex && currentCell[1] == chunkIndex * 2 + 1}
isModifiedCell={rowStatus.modifiedFields && rowStatus.modifiedFields.has(col.uniqueName)}
ref={(element) => setCellRef(rowIndex, chunkIndex * 2 + 1, element)} ref={(element) => setCellRef(rowIndex, chunkIndex * 2 + 1, element)}
> >
{inplaceEditorState.cell && {inplaceEditorState.cell &&
@@ -328,7 +337,9 @@ export default function FormView(props) {
inplaceEditorState={inplaceEditorState} inplaceEditorState={inplaceEditorState}
dispatchInsplaceEditor={dispatchInsplaceEditor} dispatchInsplaceEditor={dispatchInsplaceEditor}
cellValue={rowData[col.uniqueName]} cellValue={rowData[col.uniqueName]}
onSetValue={(value) => {}} onSetValue={(value) => {
former.setCellValue(col.uniqueName, value);
}}
// grider={grider} // grider={grider}
// rowIndex={rowIndex} // rowIndex={rowIndex}
// uniqueName={col.uniqueName} // uniqueName={col.uniqueName}

View File

@@ -0,0 +1,53 @@
// export interface GriderRowStatus {
// status: 'regular' | 'updated' | 'deleted' | 'inserted';
// modifiedFields?: Set<string>;
// insertedFields?: Set<string>;
// deletedFields?: Set<string>;
// }
export default abstract class Former {
public rowData: any;
// getRowStatus(index): GriderRowStatus {
// const res: GriderRowStatus = {
// status: 'regular',
// };
// return res;
// }
beginUpdate() {}
endUpdate() {}
setCellValue(uniqueName: string, value: any) {}
revertRowChanges() {}
revertAllChanges() {}
undo() {}
redo() {}
get editable() {
return false;
}
get canInsert() {
return false;
}
get allowSave() {
return this.containsChanges;
}
get canUndo() {
return false;
}
get canRedo() {
return false;
}
get containsChanges() {
return false;
}
get disableLoadNextPage() {
return false;
}
get errors() {
return null;
}
updateRow(changeObject) {
for (const key of Object.keys(changeObject)) {
this.setCellValue(key, changeObject[key]);
}
}
}

View File

@@ -5,6 +5,7 @@ import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
import useExtensions from '../utility/useExtensions'; import useExtensions from '../utility/useExtensions';
import FormView from './FormView'; import FormView from './FormView';
import axios from '../utility/axios'; import axios from '../utility/axios';
import ChangeSetFormer from './ChangeSetFormer';
async function loadRow(props, sql) { async function loadRow(props, sql) {
const { conid, database } = props; const { conid, database } = props;
@@ -26,7 +27,7 @@ async function loadRow(props, sql) {
} }
export default function SqlFormView(props) { export default function SqlFormView(props) {
const { formDisplay } = props; const { formDisplay, changeSetState, dispatchChangeSet } = props;
const [rowData, setRowData] = React.useState(null); const [rowData, setRowData] = React.useState(null);
const handleLoadCurrentRow = async () => { const handleLoadCurrentRow = async () => {
@@ -46,7 +47,14 @@ export default function SqlFormView(props) {
if (formDisplay && !formDisplay.isLoadedCurrentRow(rowData)) { if (formDisplay && !formDisplay.isLoadedCurrentRow(rowData)) {
handleLoadCurrentRow(); handleLoadCurrentRow();
} }
}, [formDisplay]); }, [formDisplay, rowData]);
const former = React.useMemo(() => new ChangeSetFormer(rowData, changeSetState, dispatchChangeSet, formDisplay), [
rowData,
changeSetState,
dispatchChangeSet,
formDisplay,
]);
// const { config, setConfig, cache, setCache, schemaName, pureName, conid, database } = props; // const { config, setConfig, cache, setCache, schemaName, pureName, conid, database } = props;
// const { formViewKey } = config; // const { formViewKey } = config;
@@ -76,5 +84,5 @@ export default function SqlFormView(props) {
// setDisplay(newDisplay); // setDisplay(newDisplay);
// }, [config, cache, conid, database, schemaName, pureName, dbinfo, extensions]); // }, [config, cache, conid, database, schemaName, pureName, dbinfo, extensions]);
return <FormView {...props} rowData={rowData} onNavigate={handleNavigate} />; return <FormView {...props} rowData={rowData} onNavigate={handleNavigate} former={former} />;
} }