inplace editing

This commit is contained in:
Jan Prochazka 2020-03-22 18:48:48 +01:00
parent b893b344f5
commit ce1e58dbdc
5 changed files with 96 additions and 31 deletions

View File

@ -22,18 +22,21 @@ export function createChangeSet(): ChangeSet {
};
}
export interface ChangeSetFieldDefinition {
export interface ChangeSetRowDefinition {
pureName: string;
schemaName: string;
uniqueName: string;
columnName: string;
insertedRowIndex?: number;
condition?: { [column: string]: string };
}
function findExistingChangeSetItem(
export interface ChangeSetFieldDefinition extends ChangeSetRowDefinition {
uniqueName: string;
columnName: string;
}
export function findExistingChangeSetItem(
changeSet: ChangeSet,
definition: ChangeSetFieldDefinition
definition: ChangeSetRowDefinition
): [keyof ChangeSet, ChangeSetItem] {
if (definition.insertedRowIndex != null) {
return [

View File

@ -4,7 +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';
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
export interface DisplayColumn {
schemaName: string;
@ -372,4 +372,13 @@ export abstract class GridDisplay {
condition: this.getChangeSetCondition(row),
};
}
getChangeSetRow(row): ChangeSetRowDefinition {
if (!this.baseTable) return null;
return {
pureName: this.baseTable.pureName,
schemaName: this.baseTable.schemaName,
condition: this.getChangeSetCondition(row),
};
}
}

View File

@ -100,6 +100,7 @@ export default function DataGridCore(props) {
const [shiftDragStartCell, setShiftDragStartCell] = React.useState(nullCell);
const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell);
const [inplaceEditorInitText, setInplaceEditorInitText] = React.useState('');
const loadNextData = async () => {
if (isLoading) return;
@ -291,7 +292,7 @@ export default function DataGridCore(props) {
if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorCell) && _.isEqual(cell, currentCell)) {
setInplaceEditorCell(cell);
} else if (!_.isEqual(cell, inplaceEditorCell)) {
setInplaceEditorCell(null);
handleCloseInplaceEditor();
}
}
@ -334,14 +335,28 @@ export default function DataGridCore(props) {
}
function handleGridKeyDown(event) {
handleCursorMove(event);
if (
!event.ctrlKey &&
!event.altKey &&
((event.keyCode >= keycodes.a && event.keyCode <= keycodes.z) ||
(event.keyCode >= keycodes.n0 && event.keyCode <= keycodes.n9) ||
event.keyCode == keycodes.dash)
) {
setInplaceEditorInitText(event.nativeEvent.key);
setInplaceEditorCell(currentCell);
// console.log('event', event.nativeEvent);
}
if (event.shiftKey) {
if (!isRegularCell(shiftDragStartCell)) {
setShiftDragStartCell(currentCell);
const moved = handleCursorMove(event);
if (moved) {
if (event.shiftKey) {
if (!isRegularCell(shiftDragStartCell)) {
setShiftDragStartCell(currentCell);
}
} else {
setShiftDragStartCell(nullCell);
}
} else {
setShiftDragStartCell(nullCell);
}
const newCell = handleCursorMove(event);
@ -480,6 +495,11 @@ export default function DataGridCore(props) {
return false;
}
function handleCloseInplaceEditor() {
setInplaceEditorCell(null);
setInplaceEditorInitText(null);
}
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
// console.log('gridScrollAreaHeight', gridScrollAreaHeight);
// console.log('containerHeight', containerHeight);
@ -599,6 +619,8 @@ export default function DataGridCore(props) {
rowHeight={rowHeight}
visibleRealColumns={visibleRealColumns}
inplaceEditorCell={inplaceEditorCell}
inplaceEditorInitText={inplaceEditorInitText}
onCloseInplaceEditor={handleCloseInplaceEditor}
cellIsSelected={cellIsSelected}
changeSet={changeSet}
setChangeSet={setChangeSet}

View File

@ -1,3 +1,4 @@
// @ts-nocheck
import moment from 'moment';
import _ from 'lodash';
import React from 'react';
@ -11,15 +12,7 @@ import axios from '../utility/axios';
import ColumnLabel from './ColumnLabel';
import DataFilterControl from './DataFilterControl';
import { getFilterType } from '@dbgate/filterparser';
import {
convertCellAddress,
cellFromEvent,
getCellRange,
topLeftCell,
isRegularCell,
nullCell,
emptyCellArray,
} from './selection';
import { findExistingChangeSetItem } from '@dbgate/datalib';
import keycodes from '../utility/keycodes';
import InplaceEditor from './InplaceEditor';
@ -31,13 +24,23 @@ const TableBodyCell = styled.td`
white-space: nowrap;
overflow: hidden;
${props =>
// @ts-ignore
props.isSelected &&
`
background: initial;
background-color: deepskyblue;
color: white;`}
`;
${props =>
props.isModifiedRow &&
!props.isSelected &&
!props.isModifiedCell &&
`
background-color: #FFFFDB;`}
${props =>
!props.isSelected &&
props.isModifiedCell &&
`
background-color: bisque;`}
`;
const HintSpan = styled.span`
color: gray;
margin-left: 5px;
@ -77,12 +80,17 @@ export default function DataGridRow({
rowIndex,
visibleRealColumns,
inplaceEditorCell,
inplaceEditorInitText,
onCloseInplaceEditor,
cellIsSelected,
row,
display,
changeSet,
setChangeSet,
}) {
const rowDefinition = display.getChangeSetRow(row);
const [_fld, matchedChangeSetItem] = findExistingChangeSetItem(changeSet, rowDefinition);
const rowUpdated = matchedChangeSetItem ? { ...row, ...matchedChangeSetItem.fields } : row;
return (
<TableBodyRow style={{ height: `${rowHeight}px` }}>
<TableHeaderCell data-row={rowIndex} data-col="header">
@ -100,19 +108,25 @@ export default function DataGridRow({
data-col={col.colIndex}
// @ts-ignore
isSelected={cellIsSelected(rowIndex, col.colIndex)}
isModifiedRow={!!matchedChangeSetItem}
isModifiedCell={matchedChangeSetItem && col.uniqueName in matchedChangeSetItem.fields}
>
{inplaceEditorCell && rowIndex == inplaceEditorCell[0] && col.colIndex == inplaceEditorCell[1] ? (
<InplaceEditor
widthPx={col.widthPx}
value={row[col.uniqueName]}
value={inplaceEditorInitText || rowUpdated[col.uniqueName]}
selectAll={!inplaceEditorInitText}
changeSet={changeSet}
setChangeSet={setChangeSet}
definition={display.getChangeSetField(row, col.uniqueName)}
onClose={onCloseInplaceEditor}
/>
) : (
<>
<CellFormattedValue value={row[col.uniqueName]} />
{col.hintColumnName && <HintSpan>{row[col.hintColumnName]}</HintSpan>}
<CellFormattedValue value={rowUpdated[col.uniqueName]} />
{(!matchedChangeSetItem || !(col.uniqueName in matchedChangeSetItem.fields)) && col.hintColumnName && (
<HintSpan>{row[col.hintColumnName]}</HintSpan>
)}
</>
)}
</TableBodyCell>

View File

@ -1,3 +1,5 @@
// @ts-nocheck
import _ from 'lodash';
import React from 'react';
import styled from 'styled-components';
@ -12,23 +14,38 @@ const StyledInput = styled.input`
padding: 0px;
`;
export default function InplaceEditor({ widthPx, value, definition, changeSet, setChangeSet }) {
export default function InplaceEditor({ widthPx, value, definition, changeSet, setChangeSet, onClose, selectAll }) {
const editorRef = React.useRef();
const isChangedRef = React.createRef();
React.useEffect(() => {
const editor = editorRef.current;
editor.value = value;
editor.focus();
editor.select();
if (selectAll) {
editor.select();
}
}, []);
function handleBlur() {
const editor = editorRef.current;
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
if (isChangedRef.current) {
const editor = editorRef.current;
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
}
}
function handleKeyDown(event) {
switch (event.keyCode) {
case keycodes.escape:
isChangedRef.current = false;
onClose();
break;
}
}
return (
<StyledInput
onBlur={handleBlur}
ref={editorRef}
type="text"
onChange={() => (isChangedRef.current = true)}
onKeyDown={handleKeyDown}
style={{
width: widthPx,
minWidth: widthPx,