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; pureName: string;
schemaName: string; schemaName: string;
uniqueName: string;
columnName: string;
insertedRowIndex?: number; insertedRowIndex?: number;
condition?: { [column: string]: string }; condition?: { [column: string]: string };
} }
function findExistingChangeSetItem( export interface ChangeSetFieldDefinition extends ChangeSetRowDefinition {
uniqueName: string;
columnName: string;
}
export function findExistingChangeSetItem(
changeSet: ChangeSet, changeSet: ChangeSet,
definition: ChangeSetFieldDefinition definition: ChangeSetRowDefinition
): [keyof ChangeSet, ChangeSetItem] { ): [keyof ChangeSet, ChangeSetItem] {
if (definition.insertedRowIndex != null) { if (definition.insertedRowIndex != null) {
return [ return [

View File

@ -4,7 +4,7 @@ import { ForeignKeyInfo, TableInfo, ColumnInfo, DbType } from '@dbgate/types';
import { parseFilter, getFilterType } from '@dbgate/filterparser'; import { parseFilter, getFilterType } from '@dbgate/filterparser';
import { filterName } from './filterName'; import { filterName } from './filterName';
import { Select, Expression } from '@dbgate/sqltree'; import { Select, Expression } from '@dbgate/sqltree';
import { ChangeSetFieldDefinition } from './ChangeSet'; import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
export interface DisplayColumn { export interface DisplayColumn {
schemaName: string; schemaName: string;
@ -372,4 +372,13 @@ export abstract class GridDisplay {
condition: this.getChangeSetCondition(row), 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 [shiftDragStartCell, setShiftDragStartCell] = React.useState(nullCell);
const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell); const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell);
const [inplaceEditorInitText, setInplaceEditorInitText] = React.useState('');
const loadNextData = async () => { const loadNextData = async () => {
if (isLoading) return; if (isLoading) return;
@ -291,7 +292,7 @@ export default function DataGridCore(props) {
if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorCell) && _.isEqual(cell, currentCell)) { if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorCell) && _.isEqual(cell, currentCell)) {
setInplaceEditorCell(cell); setInplaceEditorCell(cell);
} else if (!_.isEqual(cell, inplaceEditorCell)) { } else if (!_.isEqual(cell, inplaceEditorCell)) {
setInplaceEditorCell(null); handleCloseInplaceEditor();
} }
} }
@ -334,14 +335,28 @@ export default function DataGridCore(props) {
} }
function handleGridKeyDown(event) { 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) { const moved = handleCursorMove(event);
if (!isRegularCell(shiftDragStartCell)) {
setShiftDragStartCell(currentCell); if (moved) {
if (event.shiftKey) {
if (!isRegularCell(shiftDragStartCell)) {
setShiftDragStartCell(currentCell);
}
} else {
setShiftDragStartCell(nullCell);
} }
} else {
setShiftDragStartCell(nullCell);
} }
const newCell = handleCursorMove(event); const newCell = handleCursorMove(event);
@ -480,6 +495,11 @@ export default function DataGridCore(props) {
return false; return false;
} }
function handleCloseInplaceEditor() {
setInplaceEditorCell(null);
setInplaceEditorInitText(null);
}
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound); // console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
// console.log('gridScrollAreaHeight', gridScrollAreaHeight); // console.log('gridScrollAreaHeight', gridScrollAreaHeight);
// console.log('containerHeight', containerHeight); // console.log('containerHeight', containerHeight);
@ -599,6 +619,8 @@ export default function DataGridCore(props) {
rowHeight={rowHeight} rowHeight={rowHeight}
visibleRealColumns={visibleRealColumns} visibleRealColumns={visibleRealColumns}
inplaceEditorCell={inplaceEditorCell} inplaceEditorCell={inplaceEditorCell}
inplaceEditorInitText={inplaceEditorInitText}
onCloseInplaceEditor={handleCloseInplaceEditor}
cellIsSelected={cellIsSelected} cellIsSelected={cellIsSelected}
changeSet={changeSet} changeSet={changeSet}
setChangeSet={setChangeSet} setChangeSet={setChangeSet}

View File

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

View File

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